Rust Generic
Table of Contents
1. Rust Generic
1.1. scenario
1.1.1. struct
#![feature(core_intrinsics)] fn type_of<T>(_: &T) -> &str { unsafe { std::intrinsics::type_name::<T>() } } struct Point<T> { x: T, y: T, } struct Point2<T, U> { x: T, y: U, } fn main() { let p = Point { x: 0, y: 0 }; let p2 = Point2 { x: 0, y: 0f64 }; println!("{:?} {:?}", type_of(&p), type_of(&p2)); }
"d41d8cd98f::Point<i32>" "d41d8cd98f::Point2<i32, f64>"
1.1.2. enum
enum Option<T> { Some(T), None, }
1.1.3. function
fn nop<T>(x: T) -> () { let y = x; }
1.1.4. method
1.1.4.1. method 自身有泛型参数
struct Point { x: i32, y: i32, } impl Point { fn nop<T>(&self, x: T) -> () {} } fn main() { let p = Point { x: 0, y: 0 }; p.nop(0); }
1.1.4.2. method 使用 struct 的泛型参数
struct Point<T> { x: T, y: T, } impl<T> Point<T> { fn nop(&self, x: T) -> () {} } fn main() { let p = Point { x: 0, y: 0 }; p.nop(10); }
1.1.4.3. impl<T> 与 impl
struct Point<T> { x:T, y:T, } impl Point<i32> { fn sum (&self) -> i32 { self.x + self.y } } // impl<T> 是为了区别 Point<T> 与 Point<i32> impl<T> Point<T> { fn nop (&self) -> () { } }
1.1.5. trait
trait 与 struct 一样也支持泛型参数
trait Sum<T> { fn sum(self) -> T; } struct Point { x: i32, y: i32, } impl Sum<i32> for Point { fn sum(self) -> i32 { self.x + self.y } } fn main() { let p = Point { x: 1, y: 2 }; println!("{:?}", p.sum()); }
1.1.6. lifetime
1.2. trait bounds
针对 method 和 function 的模板的特化 (specialization) 有两种方式:
直接指定具体的类型 (concrete type)
struct Point<T> { x: T, y: T, } impl Point<i32> { fn dump(&self) { println!("{:?}", (self.x, self.y)); } } fn main() { let p = Point { x: 1, y: 2 }; p.dump(); }
trait bounds
struct Point<T> { x: T, y: T, } impl<T> Point<T> { fn dump(&self) { println!("{:?}", (self.x, self.y)); } } fn main() { let p = Point { x: 1, y: 2 }; println!("{:?}", p.dump()); } // error[E0277]: `T` doesn't implement `std::fmt::Debug` // --> quickrun:8:26 // | // 8 | println!("{:?}", (self.x, self.y)); // | ^^^^^^^^^^^^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` // | // = help: the trait `std::fmt::Debug` is not implemented for `T` // = help: consider adding a `where T: std::fmt::Debug` bound // = note: required because of the requirements on the impl of `std::fmt::Debug` for `(T, T)` // = note: required by `std::fmt::Debug::fmt`
我们希望把 dump 写的通用一些, 能自动 specialize 成不同的类型, 但编译错误.
因为编译器需要确保 T 类型可能被打印. 正确的作法是通过 trait bound 限定T 需要具体的能力
struct Point<T> { x: T, y: T, } // std::fmt::Debug + Copy 表示 T 类型需要 impl 两个 trait: // 1. Debug, 用来 print // 2. Copy, 因为 self 是引用, self.x 无法被 move out of self impl<T: std::fmt::Debug + Copy> Point<T> { fn dump(&self) { println!("{:?}", (self.x, self.y)); } } // 也可以把 trait bounds 写在 where 中 impl<T> Point<T> where T: std::fmt::Debug + Copy, { fn dump_reverse(&self) { println!("{:?}", (self.y, self.x)); } } fn main() { let p = Point { x: 1, y: 2 }; p.dump(); p.dump_reverse(); }
1.2.1. 函数自身泛型参数的 traits bound
traits bound 除了可以限制 struct 的泛型参数, 也可以用来限制函数自身的泛型参数
fn larger<T>(x: T, y: T) -> T where T: std::cmp::PartialOrd, { if x >= y { return x; } return y; } fn main() { println!("{:?}", larger(1, 2)); println!("{:?}", larger("abc", "abd")); }
1.2.1.1. impl trait
`impl trait` 是一种针对 trait bound 的语法糖
use core::fmt::Debug; fn dump(x: impl Debug) { println!("{:?}", x); } fn dump2<T>(x: T) where T: Debug, { println!("{:?}", x); } fn main() { dump(1); dump2(1); }
1.3. associate types
对于 trait 来说, 泛型参数可以改写为 associate type 的形式
trait Sum { type T; fn sum(self) -> Self::T; } struct Point { x: i32, y: i32, } impl Sum for Point { type T = i32; fn sum(self) -> i32 { self.x + self.y } } fn main() { let p = Point { x: 1, y: 2 }; println!("{:?}", p.sum()); }
由于 associate type 实际相当于原来的泛型参数, 所以需要使用 Sum trait 时需要指明 associate type, 例如:
fn foo<S>(x: S) -> () where S: Sum<T = i32>, { }