特征对象 #
- draw1 函数的参数是
Box<dyn Draw>
形式的特征对象,该特征对象是通过 Box::new(x) 的方式创建的 - draw2 函数的参数是 ``&dyn Draw
形式的特征对象,该特征对象是通过
&x的方式创建的
dyn` - 关键字只用在特征对象的类型声明上,在创建时无需使用 dyn
trait Draw {
fn draw(&self) -> String;
}
impl Draw for u8 {
fn draw(&self) -> String {
format!("u8: {}", *self)
}
}
impl Draw for f64 {
fn draw(&self) -> String {
format!("f64: {}", *self)
}
}
// 若 T 实现了 Draw 特征, 则调用该函数时传入的 Box<T> 可以被隐式转换成函数参数签名中的Box<dyn Draw>
fn draw1(x: Box<dyn Draw>) {
// 由于实现了 Deref 特征,Box 智能指针会自动解引用为它所包裹的值,然后调用该值对应的类型上定义的 `draw` 方法
x.draw();
}
fn draw2(x: &dyn Draw) {
x.draw();
}
fn main() {
let x = 1.1f64;
// do_something(&x);
let y = 8u8;
// x 和 y 的类型 T 都实现了 `Draw` 特征,因为 Box<T> 可以在函数调用时隐式地被转换为特征对象 Box<dyn Draw>
// 基于 x 的值创建一个 Box<f64> 类型的智能指针,指针指向的数据被放置在了堆上
draw1(Box::new(x));
// 基于 y 的值创建一个 Box<u8> 类型的智能指针
draw1(Box::new(y));
draw2(&x);
draw2(&y);
}
动态分发 #
- 泛型是在编译期完成处理的:编译器会为每一个泛型参数对应的具体类型生成一份代码,这种方式是静态分发(static dispatch),因为是在编译期完成的,对于运行期性能完全没有任何影响。
- 与静态分发相对应的是动态分发(dynamic dispatch),在这种情况下,直到运行时,才能确定需要调用什么方法。之前代码中的关键字 dyn 正是在强调这一“动态”的特点。
- 当使用特征对象时,Rust 必须使用动态分发。编译器无法知晓所有可能用于特征对象代码的类型,所以它也不知道应该调用哪个类型的哪个方法实现。为此,Rust 在运行时使用特征对象中的指针来知晓需要调用哪个方法。动态分发也阻止编译器有选择的内联方法代码,这会相应的禁用一些优化。
静态分发 Box<T>
和动态分发 Box<dyn Trait>
的区别
-
特征对象大小不固定:这是因为,对于特征 Draw ,类型 Button 可以实现特征 Draw ,类型SelectBox 也可以实现特征 Draw ,因此特征没有固定大小
-
几乎总是使用特征对象的引用方式,如
&dyn Draw 、 Box<dyn Draw>
- 虽然特征对象没有固定大小,但它的引用类型的大小是固定的,它由两个指针组成( ptr和 vptr ),因此占用两个指针大小
- 一个指针 ptr 指向实现了特征 Draw 的具体类型的实例,也就是当作特征 Draw 来用的类型的实例,比如类型 Button 的实例、类型 SelectBox 的实例
- 另一个指针 vptr 指向一个虚表 vtable , vtable 中保存了类型 Button 或类型SelectBox 的实例对于可以调用的实现于特征 Draw 的方法。
类型 Button 实现了特征 Draw 时,类型 Button 的实例对象 btn 可以当作特征Draw 的特征对象类型来使用, btn 中保存了作为特征对象的数据指针(指向类型 Button 的实例数据)和行为指针(指向 vtable )。
此时的 btn 是 Draw 的特征对象的实例,而不再是具体类型 Button 的实例,而且btn 的 vtable 只包含了实现自特征 Draw 的那些方法(比如 draw ),因此 btn 只能调用实现于
特征 Draw 的 draw 方法,而不能调用类型 Button 本身实现的方法和类型 Button 实现于其他特征的方法。也就是说, btn 是哪个特征对象的实例,它的 vtable 中就包含了该特征的方法
。
在外部类型上实现外部特征(newtype) #
use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
fn main() {
let w = Wrapper(vec![String::from("hello"), String::from("world")]);
println!("w = {}", w);
}
Vector 存储 #
trait IpAddr {
fn display(&self);
}
struct V4(String);
impl IpAddr for V4 {
fn display(&self) {
println!("ipv4: {:?}", self.0)
}
}
struct V6(String);
impl IpAddr for V6 {
fn display(&self) {
println!("ipv6: {:?}", self.0)
}
}
fn main() {
// Vec<Box<dynIpAddr>> ,表示数组 v 存储的是特征 IpAddr 的对象,这样就实现了在数组中存储不同的类型
let v: Vec<Box<dyn IpAddr>> = vec![
Box::new(V4("127.0.0.1".to_string())),
Box::new(V6("::1".to_string())),
];
for ip in v {
ip.display();
}
}
Vec 不稳定排序 #
// #[derive(Debug, Ord, Eq, PartialEq, PartialOrd)] 可以不适用闭包
fn main() {
let mut vec = vec![1.0, 5.6, 10.3, 2.0, 15f32];
// vec.sort_unstable();
// 在浮点数当中,存在一个 NAN 的值,这个值无法与其他的浮点数进行对比
// 因此,浮点数类型并没有实现全数值可比较 Ord 的特性,而是实现了部分可比较的特性 PartialOrd
// 使用 partial_cmp来作为大小判断的依据。
vec.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
assert_eq!(vec, vec![1.0, 2.0, 5.6, 10.3, 15f32]);
}
error[E0277]: the trait bound `f32: Ord` is not satisfied
--> exercises/intro/intro.rs:3:9
|
3 | vec.sort_unstable();
| ^^^^^^^^^^^^^ the trait `Ord` is not implemented for `f32`
|
= help: the following other types implement trait `Ord`:
isize
i8
i16
i32
i64
i128
usize
u8
and 4 others
note: required by a bound in `core::slice::<impl [T]>::sort_unstable`
悬垂指针和生命周期 #
生命周期的主要作用是避免悬垂引用,它会导致程序引用了本不该引用的数据
fn main() {
{
let r;
{
let x = 5;
r = &x;
}
println!("r: {}", r);
}
}
error[E0597]: `x` does not live long enough
--> exercises/intro/intro.rs:6:17
|
5 | let x = 5;
| - binding `x` declared here
6 | r = &x;
| ^^ borrowed value does not live long enough
7 | }
| - `x` dropped here while still borrowed
8 | println!("r: {}", r);
| - borrow later used here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0597`.
生命周期标注并不会改变任何引用的实际作用域
&i32 // 一个引用
&'a i32 // 具有显式生命周期的引用
&'a mut i32 // 具有显式生命周期的可变引用
结构体中的生命周期 #
struct ImportantExcerpt<'a> {
part: &'a str,
}
fn main() {
let novel: String = String::from("Call me Ishmael. Some years ago...");
let first_sentence: &str = novel.split('.').next().expect("Could not find a '.'");
let i: ImportantExcerpt<'_> = ImportantExcerpt {
part: first_sentence,
};
}
struct ImportantExcerpt<'a> {
part: &'a str,
}
fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
let i = ImportantExcerpt {
part: first_sentence,
};
print!("{}", i.announce_and_return_part("Hello"));
}
impl<'a> ImportantExcerpt<'a> {
fn announce_and_return_part<'b>(&'a self, announcement: &'b str) -> &'b str
where
'a: 'b,
{
println!("Attention please: {}", announcement);
self.part
}
}
返回值和错误处理 #
use std::error::Error;
use std::fs::File;
fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("info.toml")?;
Ok(())
}
// 用 ? 提前返回
// std::error:Error 是 Rust 中抽象层次最高的错误,其它标准库中的错误都实现了该特征
// 可以用该特征对象代表一切错误,就算 main 函数中调用任何标准库函数发生错误,都可以通过
// Box<dyn Error> 这个特征对象进行返回
模块使用及处理 #
// 一个名为 `my_mod` 的模块
mod my_mod {
// 模块中的项默认具有私有的可见性
fn private_function() {
println!("called `my_mod::private_function()`");
}
// 使用 `pub` 修饰语来改变默认可见性。
pub fn function() {
println!("called `my_mod::function()`");
}
// 在同一模块中,项可以访问其它项,即使它是私有的。
pub fn indirect_access() {
print!("called `my_mod::indirect_access()`, that\n> ");
private_function();
}
// 模块也可以嵌套
pub mod nested {
pub fn function() {
println!("called `my_mod::nested::function()`");
}
#[allow(dead_code)]
fn private_function() {
println!("called `my_mod::nested::private_function()`");
}
// 使用 `pub(in path)` 语法定义的函数只在给定的路径中可见。
// `path` 必须是父模块(parent module)或祖先模块(ancestor module)
pub(in crate::my_mod) fn public_function_in_my_mod() {
print!(
"called `my_mod::nested::public_function_in_my_mod()`, that\n >
"
);
public_function_in_nested()
}
// 使用 `pub(self)` 语法定义的函数则只在当前模块中可见。
pub(self) fn public_function_in_nested() {
println!("called `my_mod::nested::public_function_in_nested");
}
// 使用 `pub(super)` 语法定义的函数只在父模块中可见。
pub(super) fn public_function_in_super_mod() {
println!("called my_mod::nested::public_function_in_super_mod");
}
}
pub fn call_public_function_in_my_mod() {
print!("called `my_mod::call_public_funcion_in_my_mod()`, that\n> ");
nested::public_function_in_my_mod();
print!("> ");
nested::public_function_in_super_mod();
}
// `pub(crate)` 使得函数只在当前包中可见
pub(crate) fn public_function_in_crate() {
println!("called `my_mod::public_function_in_crate()");
}
// 嵌套模块的可见性遵循相同的规则
mod private_nested {
#[allow(dead_code)]
pub fn function() {
println!("called `my_mod::private_nested::function()`");
}
}
}
fn function() {
println!("called `function()`");
}
fn main() {
// 模块机制消除了相同名字的项之间的歧义。
function();
my_mod::function();
// 公有项,包括嵌套模块内的,都可以在父模块外部访问。
my_mod::indirect_access();
my_mod::nested::function();
my_mod::call_public_function_in_my_mod();
// pub(crate) 项可以在同一个 crate 中的任何地方访问
my_mod::public_function_in_crate();
// pub(in path) 项只能在指定的模块中访问
// 报错!函数 `public_function_in_my_mod` 是私有的
//my_mod::nested::public_function_in_my_mod();
// 模块的私有项不能直接访问,即便它是嵌套在公有模块内部的
// 报错!`private_function` 是私有的
//my_mod::private_function();
// 报错!`private_function` 是私有的
//my_mod::nested::private_function();
// 报错! `private_nested` 是私有的
// my_mod::private_nested::function();
}