主要内容包括:
- 基础概念:指针,引用,智能指针
- 智能指针
Box<T>
,Deref
和Drop
1. 基础概念
1.1 指针
指针是个通用概念,它表示内存地址这种类型,其引用或“指向”其他数据。Rust中的指针是“第一类公民”(first-class values),可以将它们移动或复制,存储到数据结构中并从函数中返回。Rust提供了多种类型的指针:
- 引用(Reference),共享引用
&
,不可变引用&mut
- 原生指针(Raw Pointer),
*const
和*mut
- 智能指针(Smart Pointer),
Box<T>
,Rc<T>
等
1.2 引用
Rust中使用&
符号表示引用,也叫引用操作符。其使用场景是只使用类型的值但不获取其所有权,同时Rust的引用规则为:
- 在作用域中的数据有且只能有一个可变引用;
- 可以有多个不可变引用;
- 不能同时拥有不可变引用和可变引用。
注:一个引用的作用域从声明的地方开始一直持续到最后一次使用为止。
参见以下示例:
fn main() {
let arr = [1,2,3];
let addr = &arr; // 通过 & 得到引用,默认是不可变的
println!("{:p}", addr); // 内存地址
let mut vec = vec![1,2,3]; // 要获取可变引用,必须先声明可变绑定
let new_vec = &mut vec; // 通过 &mut 得到可变引用
new_vec.push(4);
println!("{:?}", new_vec); // [1, 2, 3, 4]
let mut str1 = String::from("hello");
let m1 = &mut str1;
let m2 = &mut str1; // ERROR:只能有一个可变引用
println!("{}, {}", m1, m2);
println!("{}", m2); // WORK:m1 作用域结束
let mut str2 = String::from("world");
let r1 = &str2;
let r2 = &str2; // 没问题
let r3 = &mut str2;
println!("{}, {}, and {}", r1, r2, r3); // ERROR:不能同时拥有不可变引用和可变引用
println!("{}", r3); // WORK:r1 和 r2 作用域结束
}
从语义上说,不管是&
还是&mut
,都是对原有变量的所有权的借用,所以引用也被称为借用。
1.3 智能指针
智能指针的概念起源于C++,智能指针是一类数据结构,他们的表现类似指针,但是拥有额外的元数据和功能。
在Rust中,引用和智能指针的一个的区别是引用是一类只借用数据的指针;相反,在大部分情况下,智能指针拥有他们指向的数据。Rust标准库中不同的智能指针提供了比引用更丰富的功能:
Box<T>
,用于在堆上分配数据。Rc<T>
,一个引用计数类型,其数据可以有多个所有者。Ref<T>
和RefMut<T>
,通过RefCell<T>
访问,一个在运行时而不是在编译时执行借用规则的类型。
2. 智能指针Box<T>
在Rust中,所有值默认都是栈上分配。通过创建Box<T>
,可以把值装箱,使它在堆上分配。Box<T>
类型是一个智能指针,因为它实现了Deref
trait,它允许Box<T>
值被当作引用对待。当Box<T>
值离开作用域时,由于它实现了Drop
trait,首先删除其指向的堆数据,然后删除自身。
2.1 Deref
Deref
这个trait,允许我们重载解引用运算符*
。实现Deref
的智能指针可以被当作引用来对待,也就是说可以对智能指针使用*
运算符进行解引用。
Box<T>
对Deref
的实现:
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Deref for Box<T> {
type Target = T;
fn deref(&self) -> &T {
&**self
}
}
该实现返回&**self
。为什么呢?由于self
是一个&Box<T>
,因此对其进行一次解引用*
将获得一个Box<T>
,而第二次解引用*
将获得一个T
。最后,将其包装在引用&
中并返回。
注:如果是我们自定义的类型,要实现deref
,则不能仿照它,否则会造成无限递归。
2.2 Drop
Drop
这个trait的主要作用是释放实现者实例拥有的资源。它只有一个方法:drop
,当实例离开作用域时会自动调用该方法,从而调用实现者指定的代码。
Box<T>
对Drop
的实现:
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<#[may_dangle] T: ?Sized> Drop for Box<T> {
fn drop(&mut self) {
// FIXME: Do nothing, drop is currently performed by compiler.
}
}
2.3 Box<T>
Box<T>
是堆上分配的指针类型,称为“装箱”(boxed),其指针本身在栈,指向的数据在堆,在Rust中提供了最简单的堆分配类型。使用Box<T>
的情况:
- 递归类型和trait对象。Rust需要在编译时知道一个类型占用多少空间,
Box<T>
的大小是已知的。 - “大”的数据转移所有权。用
Box<T>
只需拷贝指针。
递归类型的经典示例:
use List::{Cons, Nil};
#[derive(Debug)]
enum List<T> {
Cons(T, Box<List<T>>),
Nil,
}
fn main() {
let recursive_list: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Nil))));
println!("{:?}", recursive_list); // 打印出:Cons(1, Cons(2, Nil))
}
trait对象的示例:
trait T {
fn m(&self) -> u64;
}
struct S {
i: u64
}
impl T for S {
fn m(&self) -> u64 { self.i }
}
fn f(x: Box<dyn T>) {
println!("{}", x.m())
}
fn main() {
let s = S{i : 100};
println!("{}", s.m());
let b: Box<S> = Box::new(S{i: 100});
f(b);
}
本文示例代码:https://github.com/lesterli/rust-practice/tree/master/head-first/std-box
评论区
写评论还没有评论