总所周知学习 Java 逃不开对各类设计模式的理解运用。今天千里冰封介绍了一个全新的设计模式——“Tagless Final” Style, 它可以用 trait
在 Rust 中模拟子类型。
第一步实现目标
实现一个 expr 范型方法,可以根据类型执行。如下方代码,可以已 u32
或者 String
的类型返回
1fn main() {2 fn expr<T: Expr>() -> T {3 Expr::add(4 Expr::div(Expr::lit(100), Expr::lit(10)),5 Expr::sub(Expr::lit(223), Expr::lit(23)),6 )7 }8 println!("{}", expr::<u32>());9 println!("{}", expr::<String>());10}
output:
1/Users/edward/.cargo/bin/cargo run --color=always --package tagless --bin tagless2 Compiling tagless v0.1.0 (/Users/edward/github/SASUKE40/tagless)3 Finished dev [unoptimized + debuginfo] target(s) in 0.39s4 Running `target/debug/tagless`52106((100 / 10) + (223 - 23))
基础的 Expr trait 定义
这个最基础的 trait
是外部引入,不能更改的。
1pub(crate) trait Expr {2 fn lit(i: u32) -> Self;3 fn add(lhs: Self, rhs: Self) -> Self;4 fn sub(lhs: Self, rhs: Self) -> Self;5 fn mul(lhs: Self, rhs: Self) -> Self;6 fn div(lhs: Self, rhs: Self) -> Self;7}
实现 String 和 u32 的 impl
1impl Expr for u32 {2 fn lit(i: u32) -> Self {3 i4 }5 fn add(lhs: Self, rhs: Self) -> Self {6 lhs + rhs7 }8 fn sub(lhs: Self, rhs: Self) -> Self {9 lhs - rhs10 }11 fn mul(lhs: Self, rhs: Self) -> Self {12 lhs * rhs13 }14 fn div(lhs: Self, rhs: Self) -> Self {15 lhs / rhs16 }17}1819impl Expr for String {20 fn lit(i: u32) -> Self {21 i.to_string()22 }23 fn add(lhs: Self, rhs: Self) -> Self {24 format!("({} + {})", lhs, rhs)25 }26 fn sub(lhs: Self, rhs: Self) -> Self {27 format!("({} - {})", lhs, rhs)28 }29 fn mul(lhs: Self, rhs: Self) -> Self {30 format!("({} * {})", lhs, rhs)31 }32 fn div(lhs: Self, rhs: Self) -> Self {33 format!("({} / {})", lhs, rhs)34 }35}
以上的内容都单独抽出放入到 expr.rs
中。
给 expr 增加 exp 方法
因为 Expr trait 不可被更改,所以新增一个 trait
的 TypeParamBounds1: Rust traits
1Syntax2Trait :3 unsafe? trait IDENTIFIER Generics? ( : TypeParamBounds? )? WhereClause? {4 TraitItem*5 }
定义一个新的 ExprWithExp
trait,并实现 u32
和 String
类型的 exp
方法:
1trait ExprWithExp: Expr {2 fn exp(lhs: Self, rhs: Self) -> Self;3}45impl ExprWithExp for u32 {6 fn exp(lhs: Self, rhs: Self) -> Self {7 lhs.pow(rhs)8 }9}1011impl ExprWithExp for String {12 fn exp(lhs: Self, rhs: Self) -> Self {13 format!("({} ^ {})", lhs, rhs)14 }15}
这样就可以给 u32
和 String
拓展出 exp
方法
1fn main() {2 // snippet3 fn expr2<T: ExprWithExp>() -> T {4 ExprWithExp::exp(expr(), Expr::lit(2))5 }67 println!("{}", expr2::<u32>());8 println!("{}", expr2::<String>());9}
output:
1/Users/edward/.cargo/bin/cargo run --color=always --package tagless --bin tagless2 Compiling tagless v0.1.0 (/Users/edward/github/SASUKE40/tagless)3 Finished dev [unoptimized + debuginfo] target(s) in 0.56s4 Running `target/debug/tagless`52106((100 / 10) + (223 - 23))7441008(((100 / 10) + (223 - 23)) ^ 2)