原文链接:(*´∇`*) 咦,又好了~ Rust – xiaocr_bloghttp://www.xiaocr.fun/index.php/2024/03/18/trait%E4%B8%8E%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/
目录
Trait
定义trait
默认实现
trait作为参数
Trait Bound语法
通过+指定多个 trait bound
通过where简化 trait bound
返回实现了 trait 的类型
生命周期避免了悬垂引用
函数中泛型生命周期
生命周期标记语法
函数签名中的生命周期标注
结构体定义中生命周期标注
生命周期省略
静态生命周期
结合泛型类型参数、trait bounds 和生命周期
😃Trait
trait 告诉 Rust 编译器某个特定类型拥有可能与其他类型共享的功能。可以通过 trait 以一种抽象的方式定义共享的行为
注意:trait 类似于其他语言中常被称为 接口(interfaces)的功能,虽然有一些不同。
❗️定义trait
一个类型的行为由其可供调用的方法构成。如果可以对不同类型调用相同的方法的话,这些类型就可以共享相同的行为了。trait 定义是一种将方法签名组合起来的方法,目的是定义一个实现某些目的所必需的行为的集合。
pub trait Summary { fn summarize(&self) -> String; }
这里使用 trait 关键字来声明一个 trait,后面是 trait 的名字,在这个例子中是 Summary。在大括号中声明描述实现这个 trait 的类型所需要的行为的方法签名,在这个例子中是 fn summarize(&self) -> String。
默认实现
fn main() { pub trait Summary { fn summarize(&self) -> String { String::from("(Read more...)") } } }
trait作为参数
使用 trait 来接受多种不同类型的参数
pub fn notify(item: impl Summary) { println!("Breaking news! {}", item.summarize()); }
对于 item 参数,我们指定了 impl 关键字和 trait 名称,而不是具体的类型。该参数支持任何实现了指定 trait 的类型。在 notify 函数体中,可以调用任何来自 Summary trait 的方法,比如 summarize。我们可以传递任何 Newsarticle 或 Tweet 的实例来调用 notify。任何用其它如 String 或 i32 的类型调用该函数的代码都不能编译,因为它们没有实现 Summary。
Trait Bound语法
impl Trait 语法适用于直观的例子,它实际上是一种较长形式语法的语法糖。我们称为 trait bound,它看起来像:
pub fn notify(item: T) { println!("Breaking news! {}", item.summarize()); }
通过+指定多个 trait bound
如果 notify 需要显示 item 的格式化形式,同时也要使用 summarize 方法,那么 item 就需要同时实现两个不同的 trait:Display 和 Summary。这可以通过 + 语法实现:
pub fn notify(item: impl Summary + Display) {
+ 语法也适用于泛型的 trait bound:
pub fn notify(item: T) {
通过指定这两个 trait bound,notify 的函数体可以调用 summarize 并使用 {} 来格式化 item。
通过where简化 trait bound
每个泛型有其自己的 trait bound,所以有多个泛型参数的函数在名称和参数列表之间会有很长的 trait bound 信息,这使得函数签名难以阅读。为此,Rust 有另一个在函数签名之后的 where 从句中指定 trait bound 的语法
fn some_function(t: T, u: U) -> i32 where T: Display + Clone, U: Clone + Debug {
返回实现了 trait 的类型
也可以在返回值中使用 impl Trait 语法,来返回实现了某个 trait 的类型:
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, } }
生命周期
生命周期的概念从某种程度上说不同于其他语言中类似的工具,毫无疑问这是 Rust 最与众不同的功能。
生命周期避免了悬垂引用
生命周期的主要目标是避免悬垂引用,它会导致程序引用了非预期引用的数据
{ let r; { let x = 5; r = &x; } println!("r: {}", r); }
会出现错误,:“x dose not live long enough
函数中泛型生命周期
下面这个代码是会报错的
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(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y } }
因为 Rust 并不知道将要返回的引用是指向 x 或 y。事实上我们也不知道,因为函数体中 if 块返回一个 x 的引用而 else 块返回一个 y 的引用!
当我们定义这个函数的时候,并不知道传递给函数的具体值,所以也不知道到底是 if 还是 else 会被执行。我们也不知道传入的引用的具体生命周期
生命周期标记语法
生命周期标注并不改变任何引用的生命周期的长短。与当函数签名中指定了泛型类型参数后就可以接受任何类型一样,当指定了泛型生命周期后函数也能接受任何生命周期的引用。生命周期标注描述了多个引用生命周期相互的关系,而不影响其生命周期。
&i32 // 引用 &'a i32 // 带有显式生命周期的引用 &'a mut i32 // 带有显式生命周期的可变引用
函数签名中的生命周期标注
就像泛型类型参数,泛型生命周期参数需要声明在函数名和参数列表间的尖括号中
fn longest y.len() { x } else { y } }
结构体定义中生命周期标注
需要为结构体定义中的每一个引用添加生命周期标注
struct ImportantExcerpt &'a i32。
静态生命周期
'static,其生命周期能够存活于整个程序期间。所有的字符串字面量都拥有 'static 生命周期
fn main() { let s: &'static str = "I have a static lifetime."; }
结合泛型类型参数、trait bounds 和生命周期
fn main() { use std::fmt::Display; fn longest_with_an_announcement y.len() { x } else { y } } }