目录
定义特征
pub trait Summary { fn summarize(&self) -> String; }
为类型实现特征
pub trait Summary { fn summarize(&self) -> String; } pub struct Post { pub title: String, // 标题 pub author: String, // 作者 pub content: String, // 内容 } impl Summary for Post { fn summarize(&self) -> String { format!("文章{}, 作者是{}", self.title, self.author) } } pub struct Weibo { pub username: String, pub content: String } impl Summary for Weibo { fn summarize(&self) -> String { format!("{}发表了微博{}", self.username, self.content) } }
特征定义与实现的位置:"孤儿规则"
原则:如果你想要为类型 A 实现特征 T,那么 A 或者 T 至少有一个是在当前作用域中定义的!
例如我们可以为上面的 Post 类型实现标准库中的 Display 特征,这是因为 Post 类型定义在当前的作用域中。同时,我们也可以在当前包中为 String 类型实现 Summary 特征,因为 Summary 定义在当前作用域中。
但是你无法在当前作用域中,为 String 类型实现 Display 特征,因为它们俩都定义在标准库中,其定义所在的位置都不在当前作用域,跟你半毛钱关系都没有,看看就行了。
默认实现
pub trait Summary { fn summarize(&self) -> String { String::from("(Read more...)") } } impl Summary for Post {} impl Summary for Weibo { fn summarize(&self) -> String { format!("{}发表了微博{}", self.username, self.content) } }
部分默认实现,部分自己实现
pub trait Summary { fn summarize_author(&self) -> String; fn summarize(&self) -> String { format!("(Read more from {}...)", self.summarize_author()) } } impl Summary for Weibo { fn summarize_author(&self) -> String { format!("@{}", self.username) } } println!("1 new weibo: {}", weibo.summarize());
使用特征作为函数参数
pub fn notify(item: &impl Summary) { println!("Breaking news! {}", item.summarize()); }
特征约束(trait bound)
pub fn notify<T: Summary>(item: &T) { println!("Breaking news! {}", item.summarize()); }
pub fn notify(item1: &impl Summary, item2: &impl Summary) {} // 下面这种更容易书写 pub fn notify<T: Summary>(item1: &T, item2: &T) {}
多重约束
pub fn notify(item: &(impl Summary + Display)) {} // 下面这种更容易书写 pub fn notify<T: Summary + Display>(item: &T) {}
Where 约束
// 当特征约束变得很多时,函数的签名将变得很复杂: fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 {} // 通过where改进 fn some_function<T, U>(t: &T, u: &U) -> i32 where T: Display + Clone, U: Clone + Debug {}
使用特征约束有条件地实现方法或特征
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); } } }
函数返回中的 impl Trait
fn returns_summarizable() -> impl Summary { Weibo { username: String::from("sunface"), content: String::from( "m1 max太厉害了,电脑再也不会卡", ) } }
但是这种返回值方式有一个很大的限制:只能有一个具体的类型,如果不同条件返回不同类型则不能编译成功。
修复largest函数
只加上PartialOrd还不够,必须加上限制实现Copy 特征,否则编译器抱怨会发生move
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T { let mut largest = list[0]; for &item in list.iter() { if item > largest { largest = item; } } largest }
不受Copy限制
fn largest<T>(list: &[T]) -> T { let mut largest = list[0]; for &item in list.iter() { if item > largest { largest = item; } } largest }
或
fn largest<T: PartialOrd>(list: &[T]) -> &T { let mut largest_idx = 0; for i in 0..list.len() { if list[i] > list[largest_idx] { largest_idx = i; } } &list[largest_idx] }
通过 derive 派生特征
派生特征:https://course.rs/appendix/derive.html
调用方法需要引入特征
// Rust 又提供了一个非常便利的办法,即把最常用的标准库中的特征通过 std::prelude 模块提前引入到当前作用域中,其中包括了 std::convert::TryInto // 下面use也可以不用写 use std::convert::TryInto; fn main() { let a: i32 = 10; let b: u16 = 100; let b_ = b.try_into() .unwrap(); if a < b_ { println!("Ten is less than one hundred."); } }
例子
自定义+
操作
use std::ops::Add; // 为Point结构体派生Debug特征,用于格式化输出 #[derive(Debug)] struct Point<T: Add<T, Output = T>> { //限制类型T必须实现了Add特征,否则无法进行+操作。 x: T, y: T, } impl<T: Add<T, Output = T>> Add for Point<T> { // Add Trait必须实现Output的类型 type Output = Point<T>; fn add(self, p: Point<T>) -> Point<T> { Point{ x: self.x + p.x, y: self.y + p.y, } } } fn add<T: Add<T, Output=T>>(a:T, b:T) -> T { a + b } fn main() { let p1 = Point{x: 1.1f32, y: 1.1f32}; let p2 = Point{x: 2.1f32, y: 2.1f32}; println!("{:?}", add(p1, p2)); let p3 = Point{x: 1i32, y: 1i32}; let p4 = Point{x: 2i32, y: 2i32}; println!("{:?}", add(p3, p4)); }
自定义类型打印输出
#![allow(dead_code)] use std::fmt; use std::fmt::{Display}; #[derive(Debug,PartialEq)] enum FileState { Open, Closed, } #[derive(Debug)] struct File { name: String, data: Vec<u8>, state: FileState, } impl Display for FileState { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { FileState::Open => write!(f, "OPEN"), FileState::Closed => write!(f, "CLOSED"), } } } impl Display for File { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "<{} ({})>", self.name, self.state) } } impl File { fn new(name: &str) -> File { File { name: String::from(name), data: Vec::new(), state: FileState::Closed, } } } fn main() { let f6 = File::new("f6.txt"); //... println!("{:?}", f6); println!("{}", f6); }
实现Trait示例
struct Sheep { naked: bool, name: String } impl Sheep { fn is_naked(&self) -> bool { self.naked } fn shear(&mut self) { if self.is_naked() { // `Sheep` 结构体上定义的方法可以调用 `Sheep` 所实现的特征的方法 println!("{} is already naked...", self.name()); } else { println!("{} gets a haircut!", self.name); self.naked = true; } } } trait Animal { // 关联函数签名;`Self` 指代实现者的类型 // 例如我们在为 Pig 类型实现特征时,那 `new` 函数就会返回一个 `Pig` 类型的实例,这里的 `Self` 指代的就是 `Pig` 类型 fn new(name: String) -> Self; // 方法签名 fn name(&self) -> String; fn noise(&self) -> String; // 方法还能提供默认的定义实现 fn talk(&self) { println!("{} says {}", self.name(), self.noise()); } } impl Animal for Sheep { // `Self` 被替换成具体的实现者类型: `Sheep` fn new(name: String) -> Sheep { Sheep { name: name, naked: false } } fn name(&self) -> String { self.name.clone() } fn noise(&self) -> String { if self.is_naked() { "baaaaah?".to_string() } else { "baaaaah!".to_string() } } // 默认的特征方法可以被重写 fn talk(&self) { println!("{} pauses briefly... {}", self.name, self.noise()); } } fn main() { // 这里的类型注释时必须的 let mut dolly: Sheep = Animal::new("Dolly".to_string()); // TODO ^ 尝试去除类型注释,看看会发生什么 // 或 // let mut dolly = Sheep::new("Dolly".to_string()); dolly.talk(); dolly.shear(); dolly.talk(); }
乘法
use std::ops::Mul; // 实现 fn multiply 方法 // 如上所述,`+` 需要 `T` 类型实现 `std::ops::Add` 特征 // 那么, `*` 运算符需要实现什么特征呢? 你可以在这里找到答案: https://doc.rust-lang.org/core/ops/ fn multiply<T: Mul<T, Output = T>>(a:T, b:T) -> T { a * b } fn main() { assert_eq!(6, multiply(2u8, 3u8)); assert_eq!(5.0, multiply(1.0, 5.0)); println!("Success!") }
自定义运算符(此例为了使用派生特征)
// 修复错误,不要修改 `main` 中的代码! use std::ops; struct Foo; struct Bar; #[derive(PartialEq,Debug)] struct FooBar; #[derive(PartialEq,Debug)] struct BarFoo; // 下面的代码实现了自定义类型的相加: Foo + Bar = FooBar impl ops::Add<Bar> for Foo { type Output = FooBar; fn add(self, _rhs: Bar) -> FooBar { FooBar } } impl ops::Sub<Bar> for Foo { type Output = BarFoo; fn sub(self, _rhs: Bar) -> BarFoo { BarFoo } } fn main() { // 不要修改下面代码 // 你需要为 FooBar 派生一些特征来让代码工作 assert_eq!(Foo + Bar, FooBar); assert_eq!(Foo - Bar, BarFoo); println!("Success!") }
使用特征作为函数参数
// implement `fn summary` to make the code work // fix the errors without removing any code line trait Summary { fn summarize(&self) -> String; } #[derive(Debug)] struct Post { title: String, author: String, content: String, } impl Summary for Post { fn summarize(&self) -> String { format!("The author of post {} is {}", self.title, self.author) } } #[derive(Debug)] struct Weibo { username: String, content: String, } impl Summary for Weibo { fn summarize(&self) -> String { format!("{} published a weibo {}", self.username, self.content) } } fn main() { let post = Post { title: "Popular Rust".to_string(), author: "Sunface".to_string(), content: "Rust is awesome!".to_string(), }; let weibo = Weibo { username: "sunface".to_string(), content: "Weibo seems to be worse than Tweet".to_string(), }; summary(&post); summary(&weibo); println!("{:?}", post); println!("{:?}", weibo); } fn summary(t: &impl Summary) { let _ = t.summarize(); }
使用特征作为函数返回值
struct Sheep {} struct Cow {} trait Animal { fn noise(&self) -> String; } impl Animal for Sheep { fn noise(&self) -> String { "baaaaah!".to_string() } } impl Animal for Cow { fn noise(&self) -> String { "moooooo!".to_string() } } // Returns some struct that implements Animal, but we don't know which one at compile time. // FIX the erros here, you can make a fake random, or you can use trait object fn random_animal(random_number: f64) -> Box<dyn Animal> { if random_number < 0.5 { Box::new(Sheep {}) } else { Box::new(Cow{}) } } fn main() { let random_number = 0.234; let animal = random_animal(random_number); println!("You've randomly chosen an animal, and it says {}", animal.noise()); }
特征约束
// 填空 fn example1() { // `T: Trait` 是最常使用的方式 // `T: Fn(u32) -> u32` 说明 `T` 只能接收闭包类型的参数 struct Cacher<T: Fn(u32) -> u32> { calculation: T, value: Option<u32>, } impl<T: Fn(u32) -> u32> Cacher<T> { fn new(calculation: T) -> Cacher<T> { Cacher { calculation, value: None, } } fn value(&mut self, arg: u32) -> u32 { match self.value { Some(v) => v, None => { let v = (self.calculation)(arg); self.value = Some(v); v }, } } } let mut cacher = Cacher::new(|x| x+1); assert_eq!(cacher.value(10), 11); assert_eq!(cacher.value(15), 11); } fn example2() { // 还可以使用 `where` 来约束 T struct Cacher<T> where T: Fn(u32) -> u32, { calculation: T, value: Option<u32>, } impl<T> Cacher<T> where T: Fn(u32) -> u32, { fn new(calculation: T) -> Cacher<T> { Cacher { calculation, value: None, } } fn value(&mut self, arg: u32) -> u32 { match self.value { Some(v) => v, None => { let v = (self.calculation)(arg); self.value = Some(v); v }, } } } let mut cacher = Cacher::new(|x| x+1); assert_eq!(cacher.value(20), 21); assert_eq!(cacher.value(25), 21); } fn main() { example1(); example2(); println!("Success!") }