Rust智能指针:解锁内存管理的进阶之道在Rust编程中,内存安全是其核心优势之一,而智能指针作为Rust内存管理的关键工具,不仅提供了灵活的数据操作方式,还确保了高效和安全的内存管理。本文深入探讨Rust中智能指针的多种实现,包括Box、Rc、RefCell等,结合实际代码示例,带你掌握智能指针
在Rust编程中,内存安全是其核心优势之一,而智能指针作为Rust内存管理的关键工具,不仅提供了灵活的数据操作方式,还确保了高效和安全的内存管理。本文深入探讨Rust中智能指针的多种实现,包括Box<T>、Rc<T>、RefCell<T>等,结合实际代码示例,带你掌握智能指针的用法及其在复杂场景中的应用。无论你是Rust新手还是进阶开发者,这篇文章都将为你揭开智能指针的奥秘,助你在Rust编程中更进一步!
智能指针是Rust中一类行为类似指针但具备额外元数据和功能的数据结构,能够有效管理内存并支持复杂的数据共享场景。本文从基础概念入手,详细介绍了Box<T>在堆内存分配中的作用、Deref和Drop trait的实现原理、引用计数指针Rc<T>的多重所有权机制,以及RefCell<T>的内部可变性模式。文章还探讨了如何通过结合Rc<T>和RefCell<T>实现多重所有权的可变数据,并分析了循环引用可能导致的内存泄漏问题及使用Weak<T>的解决方法。通过代码示例和场景分析,本文旨在帮助读者全面理解Rust智能指针的强大功能及其适用场景。
String 和 Vec<T>
都拥有一片内存区域,且允许用户对其操作
还拥有元数据(例如容量等)
提供额外的功能或保障(String 保障其数据是合法的 UTF-8 编码)
Box<T>
:在 heap 内存上分配值Rc<T>
:启用多重所有权的引用计数类型Ref<T>
和RefMut<T>
,通过 RefCell<T>
访问:在运行时而不是编译时强制借用规则的类型Box<T>
来指向 Heap 上的数据Box<T>
Box<T>
是最简单的智能指针:
Box<T>
的常用场景Box<T>
在heap上存储数据fn main() {
let b = Box::new(5);
println!("b = {}", b);
} // b 释放存在 stack 上的指针 heap上的数据
use crate::List::{Cons, Nil};
fn main() {
let list = Cons(1, Cons(2, Cons(3, Nil)));
}
enum List { // 报错
Cons(i32, List),
Nil,
}
enum Message {
Quit,
Move {x: i32, y: i32},
Write(String),
ChangeColor(i32, i32, i32),
}
use crate::List::{Cons, Nil};
fn main() {
let list = Cons(1,
Box::new(Cons(2,
Box::new(Cons(3,
Box::new(Nil))))));
}
enum List {
Cons(i32, Box<List>),
Nil,
}
fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y);
}
Box<T>
当作引用Box<T>
可以替代上例中的引用fn main() {
let x = 5;
let y = Box::new(x);
assert_eq!(5, x);
assert_eq!(5, *y);
}
Box<T>
被定义成拥有一个元素的 tuple structMyBox<T>
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
fn main() {
let x = 5;
let y = MyBox::new(x); // 报错
assert_eq!(5, x);
assert_eq!(5, *y);
}
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
fn main() {
let x = 5;
let y = MyBox::new(x);
assert_eq!(5, x);
assert_eq!(5, *y); // *(y.deref())
}
use std::ops::Deref;
fn hello(name: &str) {
println!("Hello, {}", name);
}
fn main() {
let m = MyBox::new(String::from("Rust"));
// &m &MyBox<String> 实现了 deref trait
// deref &String
// deref &str
hello(&m);
hello(&(*m)[..]);
hello("Rust");
}
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
fn main() {
let x = 5;
let y = MyBox::new(x);
assert_eq!(5, x);
assert_eq!(5, *y); // *(y.deref())
}
/*
* @Author: QiaoPengjun5162 qiaopengjun0@gmail.com
* @Date: 2023-04-13 21:39:51
* @LastEditors: QiaoPengjun5162 qiaopengjun0@gmail.com
* @LastEditTime: 2023-04-13 21:46:50
* @FilePath: /smart/src/main.rs
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data: `{}`!", self.data);
}
}
fn main() {
let c = CustomSmartPointer {data: String::from("my stuff")};
let d = CustomSmartPointer {data: String::from("other stuff")};
println!("CustomSmartPointers created.")
}
运行
smart on master [?] is 📦 0.1.0 via 🦀 1.67.1
➜ cargo run
Compiling smart v0.1.0 (/Users/qiaopengjun/rust/smart)
warning: unused variable: `c`
--> src/main.rs:20:9
|
20 | let c = CustomSmartPointer {data: String::from("my stuff")};
| ^ help: if this is intentional, prefix it with an underscore: `_c`
|
= note: `#[warn(unused_variables)]` on by default
warning: unused variable: `d`
--> src/main.rs:21:9
|
21 | let d = CustomSmartPointer {data: String::from("other stuff")};
| ^ help: if this is intentional, prefix it with an underscore: `_d`
warning: `smart` (bin "smart") generated 2 warnings (run `cargo fix --bin "smart"` to apply 2 suggestions)
Finished dev [unoptimized + debuginfo] target(s) in 0.53s
Running `target/debug/smart`
CustomSmartPointers created.
Dropping CustomSmartPointer with data: `other stuff`!
Dropping CustomSmartPointer with data: `my stuff`!
smart on master [?] is 📦 0.1.0 via 🦀 1.67.1 took 3.6s
std::mem::drop
来提前 drop 值std::mem::drop
函数,来提前 drop 值struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data: `{}`!", self.data);
}
}
fn main() {
let c = CustomSmartPointer {data: String::from("my stuff")};
drop(c);
let d = CustomSmartPointer {data: String::from("other stuff")};
println!("CustomSmartPointers created.")
}
Rc<T>
:引用计数智能指针Rc<T>
引用计数智能指针Rc<T>
Rc<T>
使用场景Rc<T>
只能用于单线程场景Rc<T>
不在预导入模块(prelude)Rc::clone(&a)
函数:增加引用计数Rc::strong_count(&a)
:获得引用计数
Rc::weak_count
函数/*
* @Author: QiaoPengjun5162 qiaopengjun0@gmail.com
* @Date: 2023-04-13 22:32:41
* @LastEditors: QiaoPengjun5162 qiaopengjun0@gmail.com
* @LastEditTime: 2023-04-13 22:37:17
* @FilePath: /smart/src/lib.rs
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
enum List {
Cons(i32, Box<List>),
Nil,
}
use crate::List::{Cons, Nil};
fn main() {
let a = Cons(5, Box::new(Cons(10, Box::new(Nil))));
let b = Cons(3, Box::new(a));
let c = Cons(4, Box::new(a)); // 报错
}
优化修改一
/*
* @Author: QiaoPengjun5162 qiaopengjun0@gmail.com
* @Date: 2023-04-13 22:32:41
* @LastEditors: QiaoPengjun5162 qiaopengjun0@gmail.com
* @LastEditTime: 2023-04-13 22:45:15
* @FilePath: /smart/src/lib.rs
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
enum List {
Cons(i32, Rc<List>),
Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
// a.clone() // 深度拷贝操作
let b = Cons(3, Rc::clone(&a));
let c = Cons(4, Rc::clone(&a)); //
}
优化修改二
/*
* @Author: QiaoPengjun5162 qiaopengjun0@gmail.com
* @Date: 2023-04-13 22:32:41
* @LastEditors: QiaoPengjun5162 qiaopengjun0@gmail.com
* @LastEditTime: 2023-04-13 22:51:04
* @FilePath: /smart/src/lib.rs
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
enum List {
Cons(i32, Rc<List>),
Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
println!("count after creating a = {}", Rc::strong_count(&a));
let b = Cons(3, Rc::clone(&a));
println!("count after creating b = {}", Rc::strong_count(&a));
{
let c = Cons(4, Rc::clone(&a));
println!("count after creating c = {}", Rc::strong_count(&a));
}
println!("count after c goes of scope = {}", Rc::strong_count(&a));
}
Rc::clone()
vs 类型的 clone() 方法Rc::clone()
:增加引用,不会执行数据的深度拷贝操作Rc<T>
Rc<T>
通过不可变引用,使你可以在程序不同部分之间共享只读数据
但是,如何允许数据变化呢?
RefCell<T>
和内部可变性RefCell<T>
Rc<T>
不同, RefCell<T>
类型代表了其持有数据的唯一所有权。RefCell<T>
与 Box<T>
的区别Box<T>
RefCell<T>
编译阶段
运行时
RefCell<T>
Rc<T>
相似,只能用于单线程场景Box<T>
、Rc<T>
、RefCell<T>
的依据说明 | Box<T> |
Rc<T> |
RefCell<T> |
---|---|---|---|
同一数据的所有者 | 一个 | 多个 | 一个 |
可变性、借用检查 | 可变、不可变借用(编译时检查) | 不可变借用(编译时检查) | 可变、不可变借用(运行时检查) |
RefCell<T>
本身不可变,但仍能修改其中存储的值fn main() {
let x = 5;
let y = &mut x; // 报错 cannot borrow as mutable
}
例子:
pub trait Message {
fn send(&self, msg: &str);
}
pub struct LimitTracker<'a, T: 'a + Message> {
messenger: &'a T,
value: usize,
max: usize,
}
impl<'a, T> LimitTracker<'a, T>
where
T: Messenger,
{
pub fn new(messenger: &T, value: usize) -> LimitTracker<T> {
LimitTracker {
messenger,
value: 0,
max,
}
}
pub fn set_value(&mut self, value: usize) {
self.value = value;
let percentage_of_max = self.value as f64 / self.max as f64;
if percentage_of_max >= 1.0 {
self.messenger.send("Error: You are over your quota!");
} else if percentage_of_max >= 0.9 {
self.messenger
.send("Urgent warning: You're used up over 90% of your quota!");
} else if percentage_of_max >= 0.75 {
self.messenger
.send("Warning: You're used up over 75% of your quota!");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
struct MockMessenger {
sent_messages: Vec<String>,
}
impl MockMessenger {
fn new() -> MockMessenger {
MockMessenger {
sent_messages: vec![],
}
}
}
impl Messenger for MockMessenger {
fn send(&mut self, message: &str) { // 报错
self.sent_messages.push(String::from(message));
}
}
#[test]
fn it_sends_an_over_75_percent_warning_message() {
let mock_messenger = MockMessenger::new();
let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);
limit_tracker.set_value(80);
assert_eq!(mock_messenger.sent_messages.len(), 1);
}
}
修改之后:
pub trait Message {
fn send(&self, msg: &str);
}
pub struct LimitTracker<'a, T: 'a + Message> {
messenger: &'a T,
value: usize,
max: usize,
}
impl<'a, T> LimitTracker<'a, T>
where
T: Messenger,
{
pub fn new(messenger: &T, value: usize) -> LimitTracker<T> {
LimitTracker {
messenger,
value: 0,
max,
}
}
pub fn set_value(&mut self, value: usize) {
self.value = value;
let percentage_of_max = self.value as f64 / self.max as f64;
if percentage_of_max >= 1.0 {
self.messenger.send("Error: You are over your quota!");
} else if percentage_of_max >= 0.9 {
self.messenger
.send("Urgent warning: You're used up over 90% of your quota!");
} else if percentage_of_max >= 0.75 {
self.messenger
.send("Warning: You're used up over 75% of your quota!");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
struct MockMessenger {
sent_messages: RefCell<Vec<String>>,
}
impl MockMessenger {
fn new() -> MockMessenger {
MockMessenger {
sent_messages: RefCell::new(vec![]),
}
}
}
impl Messenger for MockMessenger {
fn send(&self, message: &str) { // 报错
self.sent_messages.borrow_mut().push(String::from(message));
}
}
#[test]
fn it_sends_an_over_75_percent_warning_message() {
let mock_messenger = MockMessenger::new();
let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);
limit_tracker.set_value(80);
assert_eq!(mock_messenger.sent_messages.borrow().len(), 1);
}
}
RefCell<T>
在运行时记录借用信息Ref<T>
,它实现了 DerefRefMut<T>
,它实现了 DerefRefCell<T>
会记录当前存在多少个活跃的 Ref<T>
和 RefMut<T>
智能指针:
Ref<T>
的值离开作用域被释放时:不可变借用计数减1RefMut<T>
的值离开作用域被释放时:可变借用计数减1Rc<T>
和 RefCell<T>
结合使用来实现一个拥有多重所有权的可变数据#[derive(Debug)]
enum List {
Cons(Rc<RefCell<i32>>, Rc<List>),
Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
use std::cell::RefCell;
fn main() {
let value = Rc::new(RefCell::new(5));
let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));
let b = Cons(Rc::new(RefCell::new(6)), Rc::clone(&a));
let c = Cons(Rc::new(RefCell::new(10)), Rc::clone(&a));
*value.borrow_mut() += 10;
println!("a after = {:?}", a);
println!("b after = {:?}", b);
println!("c after = {:?}", c);
}
运行
refdemo on master [?] is 📦 0.1.0 via 🦀 1.67.1
➜ cargo run
Compiling refdemo v0.1.0 (/Users/qiaopengjun/rust/refdemo)
Finished dev [unoptimized + debuginfo] target(s) in 0.58s
Running `target/debug/refdemo`
a after = Cons(RefCell { value: 15 }, Nil)
b after = Cons(RefCell { value: 6 }, Cons(RefCell { value: 15 }, Nil))
c after = Cons(RefCell { value: 10 }, Cons(RefCell { value: 15 }, Nil))
refdemo on master [?] is 📦 0.1.0 via 🦀 1.67.1
➜
Cell<T>
:通过复制来访问数据Mutex<T>
:用于实现跨线程情形下的内部可变性模式Rc<T>
和 RefCell<T>
就可能创造出循环引用,从而发生内存泄漏:
use crate::List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug)]
enum List {
Cons(i32, RefCell<Rc<List>>),
Nil,
}
impl List {
fn tail(&self) -> Option<&RefCell<Rc<List>>> {
match self {
Cons(_, item) => Some(item),
Nil => None,
}
}
}
fn main() {
let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));
println!("a initial rc count = {}", Rc::strong_count(&a));
println!("a next item = {:?}", a.tail());
let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));
println!("a rc count after b creation = {}", Rc::strong_count(&a));
println!("b initial rc count = {}", Rc::strong_count(&b));
println!("b next item = {:?}", b.tail());
if let Some(link) = a.tail() {
*link.borrow_mut() = Rc::clone(&b);
}
println!("b rc count after changing a = {}", Rc::strong_count(&b));
println!("a rc count after changing a = {}", Rc::strong_count(&a));
// Uncomment the next line to see that we have a cycle;
// it will overflow the stack.
// println!("a next item = {:?}", a.tail());
}
Rc<T>
换成Weak<T>
Rc::clone
为Rc<T>
实例的 strong_count 加1,Rc<T>
的实例只有在 strong_count 为0的时候才会被清理Rc<T>
实例通过调用Rc::downgrade
方法可以创建值的 Weak Reference (弱引用)
Weak<T>
(智能指针)Rc::downgrade
会为 weak_count 加 1Rc<T>
使用 weak_count 来追踪存在多少Weak<T>
Rc<T>
实例的清理Rc<T>
实例的所有权Weak<T>
前,需保证它指向的值仍然存在:
Weak<T>
实例上调用 upgrade 方法,返回Option<Rc<T>>
use std::rc::Rc;
use std::cell::RefCell;
#[derive(Debug)]
struct Node {
value: i32,
children: RefCell<Vec<Rc<Node>>>,
}
fn main() {
let leaf = Rc::new(Node {
value: 3,
children: RefCell::new(vec![]),
});
let branch = Rc::new(Node {
value: 5,
children: RefCell::new(vec![Rc::clone(&leaf)]),
});
}
修改后:
use std::rc::{ Rc, Weak };
use std::cell::RefCell;
#[derive(Debug)]
struct Node {
value: i32,
parent: RefCell<Weak<Node>>,
children: RefCell<Vec<Rc<Node>>>,
}
fn main() {
let leaf = Rc::new(Node {
value: 3,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});
println!("leaf parent - {:?}", leaf.parent.borrow().upgrade());
let branch = Rc::new(Node {
value: 5,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![Rc::clone(&leaf)]),
});
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
}
修改后:
use std::rc::{ Rc, Weak };
use std::cell::RefCell;
#[derive(Debug)]
struct Node {
value: i32,
parent: RefCell<Weak<Node>>,
children: RefCell<Vec<Rc<Node>>>,
}
fn main() {
let leaf = Rc::new(Node {
value: 3,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});
println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
{
let branch = Rc::new(Node {
value: 5,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![Rc::clone(&leaf)]),
});
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&branch),
Rc::weak_count(&branch),
);
println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
}
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
}
智能指针是Rust内存管理的核心组件,通过Box<T>、Rc<T>、RefCell<T>等类型,Rust提供了灵活而安全的内存操作方式。Box<T>适用于需要在堆上分配数据的场景,Rc<T>解决了多重所有权的问题,而RefCell<T>通过运行时借用检查实现了内部可变性。结合Rc<T>和RefCell<T>,开发者可以实现复杂的数据共享和修改逻辑,但需警惕循环引用导致的内存泄漏风险,Weak<T>则为此提供了优雅的解决方案。掌握智能指针的使用,不仅能提升Rust代码的效率和安全性,还能帮助开发者应对复杂的内存管理需求。快来动手实践,探索Rust智能指针的无限可能吧!
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!