基础 trait 介绍
Sized trait
在 Rust 里,如果一个类型的字节大小在编译期可以确定,那么这个类型就是确定大小(sized)的。确定类型的大小(size)对于能够在栈(stack)上为实例分配足够的空间是十分重要的。确定大小类型可以通过传值(by value)或者传引用(by reference)的方式来传递。
如果一个类型的大小不能在编译期确定,那么它就被称为不确定大小类型(unsized type)或者DST,即动态大小类型(Dynamically-Sized Type)。因为不确定大小类型(unsized type)不能存放在栈上,所以它们只能通过传引用(by reference)的方式来传递。
Sized trait 用于标记在编译时就能确定占用内存大小的类型。当使用泛型参数时,Rust编译器会自动为泛型参数加上 T: Sized trait bound。例如struct Foo<T>等价于struct Foo<T: Sized>。 如果在某些情况下,无法在编译时确定泛型参数T类型的大小,就需要使用?Sized标记,即如果显式加了T: ?Sized的trait bound,那么T就可以是任意大小的。
Sized trait 通常被省略,但当显式添加时,也就是说该类型的大小在编译时已知。如果一个类型的所有成员都是Sized,那么这个类型就会自动实现Sized。成员(member)的含义取决于具体的类型,例如:一个结构体的字段,枚举的变体(variants),数组的元素,元组(tuple)的项(item)等等。一旦一个类型被一个Sized实现“标记(marked)”则意味着在编译期可以确定它的字节数大小。
也就是说,除了确定大小的类型(sized type)还有不确定大小的类型(unsized type)、动态大小的类型(DST)、可能确定也可能不确定大小的类型(?sized type)、ZST(零大小类型)等。
更多知识请参考学习标准库链接:https://doc.rust-lang.org/std/marker/trait.Sized.html
类型转换trait
在前面我们讲了基本类型的一些类型转换的方式,除了使用 as 进行类型转换外,还有一些利用 trait 进行类型转换。
From 和 Into
From trait 允许一种类型定义 “怎么根据另一种类型生成自己”,因此它提供了一种类型转换的简单机制。在标准库中有无数 From 的实现,规定原生类型及其他常见类型的转换功能。
Into trait 就是把 From trait 倒过来而已。也就是说,如果你为你的类型实现了 From,那么同时你也就获得了 Into。使用 Into trait 通常要求指明要转换到的类型,因为编译器大多数时候不能推断它。
比如,可以很容易地把 &str 转换成 String:
rust
#![allow(unused)]
fn main() {
let my_str = "hello";
// 以下三个转换都依赖于一个事实:String 实现了 From<&str> 特征
let string1 = String::from(my_str);
let string2 = my_str.to_string();
let string3: String = my_str.into(); // 这里需要显式地类型标注
}TryFrom 和 TryInto
类似于 From 和 Into,TryFrom 和 TryInto 是 类型转换的通用 trait。不同于 From/Into 的是,TryFrom 和 TryInto trait 用于易出错的转换,也正因如此,其返回值是 Result 型。try_into的使用示例:
rust
fn main() {
let b: i16 = 1500;
let b_: u8 = match b.try_into() {
Ok(b1) => b1,
Err(e) => {
println!("{:?}", e.to_string());
0
}
};
}在上面的示例中,将变量b进行转换,如果转换成功那么返回,转换后的值,如果转换失败返回错误信息。
ToString 和 FromStr
要把任何类型转换成 String,只需要实现那个类型的 ToString trait。然而不要直接这么做,最好应该实现fmt::Display trait,它会自动提供 ToString,并且还可以用来打印输出。
rust
use std::fmt;
struct Circle {
radius: i32
}
// 实现 fmt::Display trait
impl fmt::Display for Circle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Circle of radius {}", self.radius)
}
}
fn main() {
let circle = Circle { radius: 6 };
println!("{}", circle.to_string());
}FromStr是 Rust 标准库中定义的 trait,当一个类型实现FromStr trait后,调用字符串的泛型函数str.parse()就可以很方便的实现字符串到某个具体类型的转换。
比如说,我们经常需要把字符串转成数字。完成这项工作的标准手段是用 parse 函数。我们得提供要转换到的类型,这可以通过不使用类型推断,或者用 “涡轮鱼” 语法(turbo fish,<>)实现。
只要对目标类型实现了 FromStr trait,就可以用 parse 把字符串转换成目标类型。 标准库中已经给无数种类型实现了 FromStr。如果要转换到用户定义类型,只要手动实现 FromStr 就行。例如下面的少诶复杂的例子:
rust
#![allow(unused)]
fn main() {
use std::str::FromStr;
use std::num::ParseIntError;
#[derive(Debug, PartialEq)]
struct Point {
x: i32,
y: i32
}
// 手动为Point结构体实现 FromStr
impl FromStr for Point {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (x, y) = s // 这里对字符串 s,进行前后切割得到元组(x, y)
.strip_prefix('(') // 采用标准库函数strip_prefix、strip_suffix、split_once等对字符串先后进行处理
.and_then(|s| s.strip_suffix(')'))
.and_then(|s| s.split_once(','))
.unwrap();
// 在RU盾他标准库中,为整型已经实现了 fromstr trait,可以直接使用parse方法
let x_fromstr = x.parse::<i32>()?;
let y_fromstr = y.parse::<i32>()?;
// 返回最后结果
Ok(Point { x: x_fromstr, y: y_fromstr })
}
}
let expected = Ok(Point { x: 1, y: 2 });
// Explicit call
assert_eq!(Point::from_str("(1,2)"), expected);
// Implicit calls, through parse
assert_eq!("(1,2)".parse(), expected);
assert_eq!("(1,2)".parse::<Point>(), expected);
}ASRef 和 AsMut
ASRef 和 AsMut用来进行轻便的、引用到引用的转换,具体来说,AsRef将一个不可变的值的引用转换为另一个不可变的引用,而AsMut对可变的引用做同样的转换。
AsRef和AsMut的定义中泛型参数T都加了T: ?Sized,所以它们都允许T使用大小可变的类型。
AsRef 提供了一个方法 .as_ref()。在这个方法中,对于一个类型为 T 的对象 foo,如果类型 T 实现了 AsRef<U>,那么,foo 可调用方法as_ref() ,即 foo.as_ref()。操作的结果,我们得到了一个类型为 &U 的新引用,也就是完成了类型的转换。例如:
rust
#![allow(unused)]
fn main() {
// 约束类型 T 实现了trait AsRef,如果没有实现的话不能进行方法调用
// 最后会转换为类型U,也就是 &str
fn is_hello<T: AsRef<str>>(s: T) {
assert_eq!("hello", s.as_ref());
}
let s: &str = "hello";
is_hello(s);
// &str和String都实现了 AsRef<str>,所以都可以进行方法调用
let s: String = "hello".to_string();
is_hello(s);
}AsMut<T> 提供了一个方法 .as_mut()。它是 AsRef<T> 的可变(mutable)引用版本。
对于一个类型为 T 的对象 foo,如果 T 实现了 AsMut<U>,那么,foo 可调用方法as_mut() 操作,即 foo.as_mut()。操作的结果,我们得到了一个类型为 &mut U 的可变(mutable)引用。
