Genéricos: tipos, traits y ciclos de vida
Comprensión
- Generic 1 : Data Type
// 泛型声明 - struct struct Point<T> { x: T, y: T, } let point = Point { x: 5, y: 4 }; // 必须是同一类型 struct PointDiffType<T, U> { x: T, y: U, } let point_diff_type = PointDiffType{ x: 5, y: 4.0 }; // 不同类型 // 泛型声明 - impl impl<T> Point<T> { fn x(&self) -> &T { &self.x } } // 泛型声明 - enum enum Option<T> { Some(T), None, } enum Result<T, E> { Ok(T), Err(E), } // 一种 mix-up 实现 struct Point<T, U> { x: T, y: U, } impl<T, U> Point<T, U> { // 由于泛型的高灵活性,这里做了巧妙的 field 交叉混合,组合出第三个新泛型实例 // 这种语法可用于 middleware fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> { Point { x: self.x, y: other.y, } } } fn main() { let p1 = Point { x: 5, y: 10.4 }; let p2 = Point { x: "Hello", y: 'c' }; let p3 = p1.mixup(p2); println!("p3.x = {}, p3.y = {}", p3.x, p3.y); } - ,
,Monomorphization process
// 比如使用了 Some 泛型代码 let integer = Some(5); let float = Some(5.0); // 编译期会 Monomorphization 为以下代码 enum Option_i32 { Some(i32), None, } enum Option_f64 { Some(f64), None, } fn main() { let integer = Option_i32::Some(5); let float = Option_f64::Some(5.0); } - Generic 2 :Traits & Trait Bounds
Trait “”,, struct、、
struct, struct method, Java
interfaceimpl structA for traitAgeneric type,: concrete type ( trait bound),impl<T: traitA + traitB + ...> structA<T> {...},:,()pub fn notify(item: &impl Summary) {...},:// Defining a Trait pub trait Summary { fn summarize(&self) -> String; } // Implementing a Trait on a Type pub struct NewsArticle { pub headline: String, pub location: String, pub author: String, pub content: String, } impl Summary for NewsArticle { fn summarize(&self) -> String { format!("{}, by {} ({})", self.headline, self.author, self.location) } } // Default Implementations pub trait Summary { fn summarize(&self) -> String { String::from("(Read more...)") } } // Default Implementations 可以调用其它 impl pub trait Summary { fn summarize_author(&self) -> String; fn summarize(&self) -> String { format!("(Read more from {}...)", self.summarize_author()) } } // Traits as Parameters pub fn notify(item: &impl Summary) { println!("Breaking news! {}", item.summarize()); } // 可以使用 generic type 来指定 trait bounds pub fn notify<T: Summary>(item1: &T, item2: &T) {} // 多个 trait 的情况 1 - 使用 + 操作符 pub fn notify(item: &(impl Summary + Display)) { pub fn notify<T: Summary + Display>(item: &T) { // 多个 trait 的情况 2 - 使用 where fn some_function<T, U>(t: &T, u: &U) -> i32 where T: Display + Clone, U: Clone + Debug // Returning Types that Implement Traits fn returns_summarizable() -> impl Summary { Tweet { username: String::from("horse_ebooks"), content: String::from( "of course, as you probably already know, people", ), reply: false, retweet: false, } } // 可以通过 trait bounds 特性,以实现:有对应 trait 的 generic type,才能有对应的 method use std::fmt::Display; struct Pair<T> { x: T, y: T, } impl<T> Pair<T> { fn new(x: T, y: T) -> Self { Self { x, y } } } impl<T: Display + PartialOrd> Pair<T> { fn cmp_display(&self) { if self.x >= self.y { println!("The largest member is x = {}", self.x); } else { println!("The largest member is y = {}", self.y); } } } - Generic 3 :Lifetimes** 3 ,, lifetimes references** The first rule is that each parameter that is a reference gets its own lifetime parameter. :。 The second rule is if there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters :,。 The third rule is if there are multiple input lifetime parameters, but one of them is
&i32 // a reference &'a i32 // a reference with an explicit lifetime &'a mut i32 // a mutable reference with an explicit lifetime // 一般用在 function 定义时 fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } // 可用于 struct struct ImportantExcerpt<'a> { part: &'a str, } // Lifetime Elision - 活用 3 条规则 fn first_word(s: &str) -> &str {...} // static 生命期,可以在整个程序运行时保持可用 let s: &'static str = "I have a static lifetime.";&selfor&mut selfbecause this is a method, the lifetime ofselfis assigned to all output lifetime parameters. :,&self&mut self(method),self。 - 3
use std::fmt::Display; fn longest_with_an_announcement<'a, T>( x: &'a str, y: &'a str, ann: T, ) -> &'a str where T: Display, { println!("Announcement! {}", ann); if x.len() > y.len() { x } else { y } }
Fuente
…
Performance of Code Using Generics
You might be wondering whether there is a runtime cost when you’re using generic type parameters. The good news is that Rust implements generics in such a way that your code doesn’t run any slower using generic types than it would with concrete types.
Rust accomplishes this by performing monomorphization of the code that is using generics at compile time. Monomorphization is the process of turning generic code into specific code by filling in the concrete types that are used when compiled.
…
The compiler uses three rules to figure out what lifetimes references have when there aren’t explicit annotations. The first rule applies to input lifetimes, and the second and third rules apply to output lifetimes. If the compiler gets to the end of the three rules and there are still references for which it can’t figure out lifetimes, the compiler will stop with an error. These rules apply to fn definitions as well as impl blocks.
The first rule is that each parameter that is a reference gets its own lifetime parameter. In other words, a function with one parameter gets one lifetime parameter: fn foo<'a>(x: &'a i32); a function with two parameters gets two separate lifetime parameters: fn foo<'a, 'b>(x: &'a i32, y: &'b i32); and so on.
The second rule is if there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters: fn foo<'a>(x: &'a i32) -> &'a i32.
The third rule is if there are multiple input lifetime parameters, but one of them is &self or &mut self because this is a method, the lifetime of self is assigned to all output lifetime parameters. This third rule makes methods much nicer to read and write because fewer symbols are necessary.