大家好,我是砸锅。一个摸鱼八年的后端开发。熟悉 Go、Lua。第二十二天还是继续和大家一起学习 Rust😊
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情
trait 的定义
trait 是 Rust 的接口,定义了类型使用这个接口的行为,将数据结构的行为单独抽取出来,使其可以在多个类型之间共享,也可以作为约束,限制参数化类型必须符合它规定的行为
例如标准库的 std::io::Write
pub trait Write {
fn write(&mut self, buf: &[u8]) -> Result<usize>;
fn flush(&mut self) -> Result<()>;
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> { ... }
fn is_write_vectored(&self) -> bool { ... }
fn write_all(&mut self, buf: &[u8]) -> Result<()> { ... }
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> Result<()> { ... }
fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result<()> { ... }
fn by_ref(&mut self) -> &mut Self
where
Self: Sized,
{ ... }
}
这些方法也被称为关联函数,在上面这个 trait 里面,write 和 flush 都是必须实现的,其他都有缺省(默认)实现
Self 和 self
Self 表示当前类型,例如 File实现了 Write,那实现过程中用到的 Self 就是指 File
self 在用作方法的第一个参数时,实际就是 self::Self 的简写,所以 &self 等于 self: &Self
,而 &mut self 等于 self: &mut 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);
}
代码里面实现了 write 和 flush 方法,其他的方法都是用缺省实现
在 Rust 里,如果无法在编译期给定一个具体类型,可以通过告诉编译器,此处需要且仅需要任何实现了这接口的数据类型,表现为 &dyn Trait 或者 Box ,这种类型叫做 Trait Object。这样就可以在运行时动态分派 (dynamic dispatching)
需要注意的是:如果 trait 所有的方法,返回值是 Self 或者携带泛型参数,那么这个 trait 就不能产生 trait object
在定义和使用 trait 时,需要遵循孤儿规则。也就是 trait 和实现 trait 的数据类型,至少有一个是当前 crate 中定义的,也就是不能为第三方的类型实现第三方的 trait
此文章为2月Day1学习笔记,内容来源于极客时间《Rust 编程第一课》