泛型、Trait、生命周期
一、提取函数消除重复
- fn main() {
- let number_list = vec![34, 50, 25, 100, 65];
- let mut largest = number_list[0];
- for number in number_list {
- if number > largest {
- largest = number;
- }
- }
-
- println!("The largest number is {}", largest);
- }
复制代码 重复代码
- fn largest(list: &[i32]) -> i32 {
- let mut largest = list[0];
- for &item in list { // &item 解构
- if item > largest {
- largest = item;
- }
- }
- largest
- }
- fn main() {
- let number_list = vec![34, 50, 25, 100, 65];
- let result = largest(&number_list);
- println!("The largest number is {}", result);
-
- let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];
- let result = largest(&number_list);
- println!("The largest number is {}", result);
- }
复制代码 消除重复的步骤
- 识别重复代码
- 提取重复代码到函数体中,并在函数签名中指定函数的输入和返回值
- 将重复的代码使用函数调用进行替代
二、泛型
泛型
- 泛型:提高代码复用能力
- 泛型是具体类型或其它属性的抽象代替:
- 你编写的代码不是最终的代码,而是一种模版,里面有一些“占位符”
- 编译器在编译时将“占位符”替换为具体的类型
- 例如:fn largest(list: &[T]) -> T {...}
- 类型参数:
- 很短,通常一个字母
- CamelCase
- T:type 的缩写
函数定义中的泛型
- fn largest<T>(list: &[T]) -> T {
- let mut largest = list[0];
- for &item in list {
- if item > largest { // 比较 报错 ToDo
- largest = item;
- }
- }
- largest
- }
- fn main() {
- let number_list = vec![34, 50, 25, 100, 65];
- let result = largest(&number_list);
- println!("The largest number is {}", result);
-
- let char_list = vec!['y', 'm', 'a', 'q'];
- let result = largest(&char_list);
- println!("The largest number is {}", result);
- }
复制代码 Struct 定义中的泛型
- struct Point<T> {
- x: T,
- y: T,
- }
- struct Point1<T, U> {
- x: T,
- y: U,
- }
- fn main() {
- let integer = Point {x: 5, y: 10};
- let float = Point(x: 1.0, y: 4.0);
-
- let integer1 = Point1 {x: 5, y: 10.0};
- }
复制代码 Enum 定义中的泛型
- enum Option<T> {
- Some(T),
- None,
- }
- enum Result<T, E> {
- Ok(T),
- Err(E),
- }
- fn main() {}
复制代码 方法定义中的泛型
- 为 struct 或 enum 实现方法的时候,可在定义中使用泛型
- struct Point<T> {
- x: T,
- y: T,
- }
- impl<T> Point<T> {
- fn x(&self) -> &T {
- &self.x
- }
- }
- impl Point<i32> {
- fn x1(&self) -> &i32 {
- &self.x
- }
- }
- fn main() {
- let p = Point {x: 5, y: 10};
- println!("p.x = {}", p.x());
- }
复制代码
- 注意:
- 把 T 放在 impl 关键字后,表示在类型 T 上实现方法
- 只针对具体类型实现方法(其余类型没实现方法):
- struct 里的泛型类型参数可以和方法的泛型类型参数不同
- struct Point<T, U> {
- x: T,
- y: U,
- }
- impl<T, U> Point<T, U> {
- 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: 4};
- let p2 = Point {x: "Hello", y: 'c'};
- let p3 = p1.mixup(p2);
-
- println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
- }
复制代码 泛型代码的性能
- 使用泛型的代码和使用具体类型的代码运行速度是一样的。
- 单态化(monomorphization):
- fn main() {
- let integer = Some(5);
- let float = Some(5.0);
- }
- 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);
- }
复制代码 三、Trait(上)
Trait
- Trait 告诉 Rust 编译器:
- Trait:抽象的定义共享行为
- Trait bounds(约束):泛型类型参数指定为实现了特定行为的类型
- Trait 与其它语言的接口(Interface)类似,但有些区别
定义一个 Trait
- Trait 的定义:把方法签名放在一起,来定义实现某种目的所必需的一组行为。
- 关键字:trait
- 只有方法签名,没有具体实现
- trait 可以有多个方法:每个方法签名占一行,以 ; 结尾
- 实现该 trait 的类型必须提供具体的方法实现
- pub trait Summary {
- fn summarize(&self) -> String;
- }
- // NewsArticle
- // Tweet
- fn main() {}
复制代码 在类型上实现 trait
- 与为类型实现方法类似
- 不同之处:
- impl xxxx for Tweet {...}
- 在 impl 的块里,需要对 Trait 里的方法签名进行具体的实现
lib.rs 文件- pub trait Summary {
- fn summarize(&self) -> String;
- }
- 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)
- }
- }
- pub struct Tweet {
- pub username: String,
- pub content: String,
- pub reply: bool,
- pub retweet: bool,
- }
- impl Summary for Tweet {
- fn summarize(&self) -> String {
- format!("{}: {}", self.username, self.content)
- }
- }
复制代码 main.rs 文件- use demo::Summary;
- use demo::Tweet;
- fn main() {
- let tweet = Tweet {
- username: String::from("horse_ebooks"),
- content: String::from("of course, as you probably already know, people"),
- reply: false,
- retweet: false,
- };
-
- println!("1 new tweet: {}", tweet.summarize())
- }
复制代码 实现 trait 的约束
- 可以在某个类型上实现某个 trait 的前提条件是:
- 这个类型或这个 trait 是在本地 crate 里定义的
- 无法为外部类型来实现外部的 trait:
- 这个限制是程序属性的一部分(也就是一致性)
- 更具体地说是孤儿规则:之所以这样命名是因为父类型不存在
- 此规则确保其他人的代码不能破坏您的代码,反之亦然
- 如果没有这个规则,两个crate 可以为同一类型实现同一个 trait,Rust就不知道应该使用哪个实现了
默认实现
lib.rs 文件- pub trait Summary {
- // fn summarize(&self) -> String;
- fn summarize(&self) -> String {
- String::from("(Read more...)")
- }
- }
- 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)
- // }
- }
- pub struct Tweet {
- pub username: String,
- pub content: String,
- pub reply: bool,
- pub retweet: bool,
- }
- impl Summary for Tweet {
- fn summarize(&self) -> String {
- format!("{}: {}", self.username, self.content)
- }
- }
复制代码 main.rs 文件- use demo::NewsArticle;
- use demo::Summary;
- fn main() {
- let article = NewsArticle {
- headline: String::from("Penguins win the Stanley Cup Championship!"),
- content: String::from("The pittsburgh penguins once again are the best hockey team in the NHL."),
- author: String::from("Iceburgh"),
- location: String::from("Pittsburgh, PA, USA"),
- };
-
- println!("1 new tweet: {}", article .summarize())
- }
复制代码
- 默认实现的方法可以调用 trait 中其它的方法,即使这些方法没有默认实现。
- pub trait Summary {
- fn summarize_author(&self) -> String;
-
- fn summarize(&self) -> String {
- format!("Read more from {} ...", self.summarize_author())
- }
- }
- pub struct NewsArticle {
- pub headline: String,
- pub location: String,
- pub author: String,
- pub content: String,
- }
- impl Summary for NewsArticle {
- fn summarize_author(&self) -> String {
- format!("@{}", self.author)
- }
- }
复制代码 四、Trait(下)
Trait 作为参数
- pub fn notify(item: impl Summary) {
- println!("Breaking news! {}", item.summarize());
- }
复制代码
- impl Trait 语法:适用于简单情况
- Trait bound 语法:可用于复杂情况
- impl Trait 语法是 Trait bound 的语法糖
- pub fn notify<T: Summary>(item: T) {
- println!("Breaking news! {}", item.summarize());
- }
复制代码- pub fn notify(item: impl Summary + Display) { println!("Breaking news! {}", item.summarize());}pub fn notify<T: Summary>(item: T) {
- println!("Breaking news! {}", item.summarize());
- }
复制代码- pub fn notify<T: Summary + Display, U: Clone + Debug>(a: T, b: U) -> String {
- format!("Breaking news! {}", a.summarize())
- }
- pub fn notify<T, U>(a: T, b: U) -> String
- where
- T: Summary + Display,
- U: Clone + Debug,
- {
- format!("Breaking news! {}", a.summarize())
- }
复制代码 实现 Trait 作为返回类型
- pub fn notify1(s: &str) -> impl Summary {
- NewsArticle {
- headline: String::from("Penguins win the Stanley Cup Championship!"),
- content: String::from("The Pittsburgh Penguins once again are the best hockey team in the NHL."),
- author: String::from("Iceburgh"),
- location: String::from("Pittsburgh, PA, USA"),
- }
- }
复制代码
- 注意: impl Trait 只能返回确定的同一种类型,返回可能不同类型的代码会报错
使用 Trait Bound 的例子
- 例子:使用 Trait Bound 修复 largest 函数
- fn largest<T: PartialOrd + Clone>(list: &[T]) -> T {
- let mut largest = list[0].clone();
-
- for item in list.iter() {
- if item > &largest { // std::cmp::ParticalOrd
- largest = item.clone();
- }
- }
-
- largest
- }
- fn main() {
- let number_list = vec![34, 50, 25, 100, 65];
- let result = largest(&number_list);
- println!("The largest number is {}", result);
-
- let char_list = vec!['y', 'm', 'a', 'q'];
- let result = largest(&char_list);
- println!("The largest char is {}", result)
- }
- fn largest<T: PartialOrd + Clone>(list: &[T]) -> &T {
- let mut largest = &list[0];
-
- for item in list.iter() {
- if item > &largest { // std::cmp::ParticalOrd
- largest = item;
- }
- }
-
- largest
- }
- fn main() {
- let str_list = vec![String::from("hello"), String::from("world")];
- let result = largest(&str_list);
- println!("The largest word is {}", result);
-
- }
复制代码 使用 Trait Bound 有条件的实现方法
- 在使用泛型类型参数的 impl 块上使用 Trait Bound,我们可以有条件的为实现了特定 Trait的类型来实现方法
- 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);
- }
- }
- }
复制代码
- 也可以为实现了其它Trait的任意类型有条件的实现某个Trait
- 为满足Trait Bound 的所有类型上实现 Trait 叫做覆盖实现(blanket implementations)
- fn main() {
- let s = 3.to_string();
- }
复制代码 五、生命周期(1/4)
生命周期
- Rust的每个引用都有自己的生命周期
- 生命周期:引用保持有效的作用域
- 大多数情况:生命周期是隐式的、可被推断的
- 当引用的生命周期可能以不同的方式互相关联时:手动标注生命周期。
生命周期 - 避免悬垂引用(dangling regerence)
- 生命周期的主要目标:避免悬垂引用(dangling regerence)
- fn main() {
- {
- let r;
- {
- let x = 5;
- r = &x; // 报错
- }
- println!("r: {}", r);
- }
- }
复制代码 借用检查器
- Rust编译器的借用检查器:比较作用域来判断所有的借用是否合法。
- fn main() {
- let x = 5;
- let r = &x;
-
- println!("r: {}", r);
- }
复制代码 函数中的泛型生命周期
- fn main() {
- let string1 = String::from("abcd");
- let string2 = "xyz";
-
- let result = longest(string1.as_str(), string2);
-
- println!("The longest string is {}", result);
- }
- fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
- if x.len() > y.len() {
- x
- } else {
- y
- }
- }
复制代码 六、生命周期(2/4)
生命周期标注语法
- 生命周期的标注不会改变引用的生命周期长度
- 当指定了泛型生命周期参数,函数可以接收带有任何生命周期的引用
- 生命周期的标注:描述了多个引用的生命周期间的关系,但不影响生命周期
生命周期标注 - 语法
- 生命周期参数名:
- 以 ' 开头
- 通常全小写且非常短
- 很多人使用 'a
- 生命周期标注的位置:
- 在引用的 & 符号后
- 使用空格将标注和引用类型分开
生命周期标注 - 例子
- &i32 // 一个引用
- &'a i32 // 带有显示生命周期的引用
- &'a mut i32 // 带有显示生命周期的可变引用
- 单个生命周期标注本身没有意义
函数签名中的生命周期标注
- 泛型生命周期参数声明在:函数名和参数列表之间的 里
- 生命周期 'a 的实际生命周期是:x 和 y 两个生命周期中较小的那个
- fn main() {
- let string1 = String::from("abcd");
- let result;
- {
- let string2 = String::from("xyz");
- let result = longest(string1.as_str(), string2.as_str()); // 报错 string2
- }
-
- println!("The longest string is {}", result);
- }
- fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
- if x.len() > y.len() {
- x
- } else {
- y
- }
- }
复制代码 七、生命周期(3/4)
深入理解生命周期
- fn main() {
- let string1 = String::from("abcd");
- let string2 = "xyz";
-
- let result = longest(string1.as_str(), string2);
-
- println!("The longest string is {}", result);
- }
- fn longest<'a>(x: &'a str, y: &str) -> &'a str {
- x
- }
复制代码
- 从函数返回引用时,返回类型的生命周期参数需要与其中一个参数的生命周期匹配
- 如果返回的引用没有指向任何参数,那么它只能引用函数内创建的值
- fn main() {
- let string1 = String::from("abcd");
- let string2 = "xyz";
-
- let result = longest(string1.as_str(), string2);
-
- println!("The longest string is {}", result);
- }
- fn longest<'a>(x: &'a str, y: &str) -> &'a str {
- let result = String::from("abc");
- result.as_str() // 报错
- }
- fn longest<'a>(x: &'a str, y: &str) -> String {
- let result = String::from("abc");
- result
- }
复制代码 Struct 定义中的生命周期标注
- Struct 里可包括:
- 自持有的类型
- 引用:需要在每个引用上添加生命周期标注
- struct ImportantExcerpt<'a> {
- part: &'a str,
- }
- fn main() {
- let novel = String::from("Call me Ishmael. Some years ago ...")
-
- let first_sentence = novel.split('.')
- .next()
- .expect("Could not found a '.'");
-
- let i = ImportantExcerpt {
- part: first_sentence
- };
- }
复制代码 静态生命周期
- 'static 是一个特殊的生命周期:整个程序的持续时间。
- 例如:所有的字符串字面值都拥有 ‘static 生命周期
- let s: &'static str = "I have a static lifetime.";
- 为引用指定 ’static 生命周期前要三思:
泛型参数类型、Trait Bound、生命周期
- struct ImportantExcerpt<'a> {
- part: &'a str,
- }
- impl<'a> ImportantExcerpt<'a> {
- fn level(&self) -> i32 {
- 3
- }
-
- fn snnounce_and_return_part(&self, announcement: &str) -> &str {
- println!("Attention please: {}", announcement);
- self.part
- }
- }
- fn main() {
- let novel = String::from("Call me Ishmael. Some years ago ...")
-
- let first_sentence = novel.split('.')
- .next()
- .expect("Could not found a '.'");
-
- let i = ImportantExcerpt {
- part: first_sentence,
- };
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |