Trait Impl/Bound: 特设多态,编译期单态化
Trait impl的两面派
其实从两个相对的角色🎭来看,就可以理解为何trait impl有两种叫法。
具体实现
当一个开发者想给类型添加方法的时候,需要先定义trait,再为类型实现trait指定方法。
设计约束
当一个设计者设计好完整流程后,为了让使用者能够保留一定空间自由度去自定义实现,就可以要求这个类型需要满足某个trait,这就是trait约束
基本练习: 使用self参数
支持泛型
版本一:支持数字相加
use std::ops::Add; #[derive(Debug)] struct Complex { real: f64, imagine: f64, } impl Complex { pub fn new(real: f64, imagine: f64) -> Self { Self { real, imagine } } } // 对 Complex 类型的实现 impl Add for Complex { type Output = Self; // 注意 add 第一个参数是 self,会移动所有权 fn add(self, rhs: Self) -> Self::Output { let real = self.real + rhs.real; let imagine = self.imagine + rhs.imagine; Self::new(real, imagine) } } fn main() { let c1 = Complex::new(1.0, 1f64); let c2 = Complex::new(2 as f64, 3.0); println!("{:?}", c1 + c2); // c1, c2 已经被移动,所以下面这句无法编译 // println!("{:?}", c1 + c2); }
支持继承
trait B:A
在 Rust 中,一个 trait 可以“继承”另一个 trait 的关联类型和关联函数。比如 trait B: A ,是说任何类型 T,如果实现了 trait B,它也必须实现 trait A,换句话说,trait B 在定义时可以使用 trait A 中的关联类型和方法。
impl<T: ?Sized> StreamExt for T where T: Stream {}
如果你实现了 Stream trait,就可以直接使用 StreamExt 里的方法了
Self和self
类比python:Self对应Cls, self两边一样。
在闭包中还要结合考虑是否转移所有权:
- self转移
- Self不转移
Self和self区别使用, Self其实就是类方法
use std::fmt; use std::io::Write; struct BufBuilder { buf: Vec<u8>, } impl BufBuilder { pub fn new() -> Self { Self { buf: Vec::with_capacity(1024), } } } impl fmt::Debug for BufBuilder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", String::from_utf8_lossy(self.buf.as_ref())) } } impl Write for BufBuilder { fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { self.buf.extend_from_slice(buf); Ok(buf.len()) } fn flush(&mut self) -> std::io::Result<()> { Ok(()) } } fn main() { let mut buf = BufBuilder::new(); buf.write_all(b"Hello world!").unwrap(); println!("{:?}", buf); }
self: Self, 实例来自于类型
- Self 代表当前的类型,比如 File 类型实现了 Write,那么实现过程中使用到的 Self 就指代 File。
- self 在用作方法的第一个参数时,实际上是 self: Self 的简写,所以 &self 是 self: &Self, 而 &mut self 是 self: &mut Self。
递进练习trait使用场景
基础使用:具体类型实现
定义Parse trait并实现使用
use regex::Regex; pub trait Parse { fn parse(s: &str) -> Self; } impl Parse for u8 { fn parse(s: &str) -> Self { let re: Regex = Regex::new(r"^[0-9]+").unwrap(); if let Some(captures) = re.captures(s) { captures .get(0) .map_or(0, |s| s.as_str().parse().unwrap_or(0)) } else { 0 } } } #[test] fn parse_should_work() { assert_eq!(u8::parse("123abcd"), 123); assert_eq!(u8::parse("1234abcd"), 0); assert_eq!(u8::parse("abcd"), 0); } fn main() { println!("result: {}", u8::parse("255 hello world")); }
- 这里的Parse Trait里面的parse方法没有传入self/Self参数,所以调用的时候使用::而不是.
- 这种基础用法中,被实现的是具体类型
进阶使用
- 这也是分离定义与实现的用处
- 下方的常用trait实现也是基于进阶使用整理出来提供的工具。
泛型实现+trait约束
impl Parse for T
use std::str::FromStr; use regex::Regex; pub trait Parse { fn parse(s: &str) -> Self; } impl<T> Parse for T where T: FromStr + Default, { fn parse(s: &str) -> Self { let re: Regex = Regex::new(r"^[0-9]+(\.[0-9]+)?").unwrap(); let d = || Default::default(); if let Some(captures) = re.captures(s) { captures .get(0) .map_or(d(), |s| s.as_str().parse().unwrap_or_else(|_| d())) } else { d() } } } #[test] fn parse_should_work() { assert_eq!(u32::parse("123abcd"), 123); assert_eq!(u32::parse("123.45abcd"), 0); assert_eq!(f64::parse("123.45abcd").to_string(), "123.45"); assert_eq!(f64::parse("abcd").to_string(), "0"); } fn main() { println!("result: {}", u8::parse("255 hello world")); }
- 对比上一个例子,这里被实现的是泛型,对于上一种用法进一步抽象
- 这样就把被实现类型从一个具体类型,扩展为一类实现了具体trait的类型,不需要重复去实现trait
这个抽象点多体会一下:从抓类型到抓实现特定trait的泛型
trait带有泛型参数+trait约束
使用一个思考题来加深印象
泛型参数impl报错
use std::io::{BufWriter, Write}; use std::net::TcpStream; #[derive(Debug)] struct MyWriter<W> { writer: W, } impl<W: Write> MyWriter<W> { pub fn new(addr: &str) -> Self { let stream = TcpStream::connect("127.0.0.1:8080").unwrap(); Self { writer: BufWriter::new(stream), } } pub fn write(&mut self, buf: &str) -> std::io::Result<()> { self.writer.write_all(buf.as_bytes()) } } fn main() { let writer = MyWriter::new("127.0.0.1:8080"); writer.write("hello world!"); }
主要原因是,实现 new 方法时,对泛型的约束要求要满足 W: Write,而 new 的声明返回值是 Self,也就是说 self.wirter 必须是 W: Write 类型(泛型),但实际返回值是一个确定的类型 BufWriter
- 修改new方法返回值
use std::io::{BufWriter, Write}; use std::net::TcpStream; #[derive(Debug)] struct MyWriter<W> { writer: W, } impl<W: Write> MyWriter<W> { pub fn new(writer: W) -> Self { Self { writer } } pub fn write(&mut self, buf: &str) -> std::io::Result<()> { self.writer.write_all(buf.as_bytes()) } } fn main() { let stream = TcpStream::connect("127.0.0.1:8080").unwrap(); let mut writer = MyWriter::new(BufWriter::new(stream)); writer.write("hello world!").unwrap(); }
- 针对实现new方法
impl MyWriter<BufWriter<TcpStream>> { pub fn new(addr: &str) -> Self { let stream = TcpStream::connect(addr).unwrap(); Self { writer: BufWriter::new(stream), } } } fn main() { let mut writer = MyWriter::new("127.0.0.1:8080"); writer.write("hello world!"); }
- 使用依赖注入修改new方法实现
impl<W: Write> MyWriter<W> { pub fn new(writer: W) -> Self { Self { writer, } } } fn main() { let stream = TcpStream::connect("127.0.0.1:8080").unwrap(); let mut writer = MyWriter::new(BufWriter::new(stream)); writer.write("hello world!"); }
补充使用:使用关联类型+添加Result<T, E>
关联类型自定义Error
use std::str::FromStr; use regex::Regex; pub trait Parse { type Error; fn parse(s: &str) -> Result<Self, Self::Error> where Self: Sized; } impl<T> Parse for T where T: FromStr + Default, { // 定义关联类型 Error 为 String type Error = String; fn parse(s: &str) -> Result<Self, Self::Error> { let re: Regex = Regex::new(r"^[0-9]+(\.[0-9]+)?").unwrap(); if let Some(captures) = re.captures(s) { // 当出错时我们返回 Err(String) captures .get(0) .map_or(Err("failed to capture".to_string()), |s| { s.as_str() .parse() .map_err(|_err| "failed to parse captured string".to_string()) }) } else { Err("failed to parse string".to_string()) } } } #[test] fn parse_should_work() { assert_eq!(u32::parse("123abcd"), Ok(123)); assert_eq!( u32::parse("123.45abcd"), Err("failed to parse captured string".into()) ); assert_eq!(f64::parse("123.45abcd"), Ok(123.45)); assert!(f64::parse("abcd").is_err()); } fn main() { println!("result: {:?}", u8::parse("255 hello world")); }
#![allow(unused)] fn main() { type Error = String; fn parse(s: &str) -> Result<Self, Self::Error> }