https://doc.rust-lang.org/book/ch15-00-smart-pointers.html

智能指针是一种想指针一样的行为且具有其他能力的数据结构(Smart pointers, on the other hand, are data structures that not only act like a pointer but also have additional metadata and capabilities.)在 Rust 中,智能指针和引用最大的区别在于:智能指针可能会拥有这个数据。
智能指针是一个结构体,并且实现了 Deref(可以像引用一样使用智能指针) 和 Drop trait(实现当智能指针离开作用域后所需要做的是)。
这里主要介绍三种常见的智能指针:

  • Box<T> for allocating values on the heap
  • Rc<T>, a reference counting type that enables multiple ownership
  • Ref<T> and RefMut<T>, accessed through RefCell<T>, a type that enforces the borrowing rules at runtime instead of compile time.

Using Box<T> to Point to Data on the Heap

Box 主要用于在堆上分配数据,没有性能开销,也没有其他能力。
Box 主要适用于以下场景:

  • When you have a type whose size can’t be known at compile time and you want to use a value of that type in a context that requires an exact size
  • When you have a large amount of data and you want to transfer ownership but ensure the data won’t be copied when you do so
  • When you want to own a value and you care only that it’s a type that implements a particular trait rather than being of a specific type

使用 Box<T> 在堆上分配空间

fn main() {let b = Box::new(5);println!("b = {}", b);
}

Enabling Recursive Types with Boxes

Recursive Types 就是一个值可以有另一个相同类型的值作为其自身的一部分。(a value can have another value of the same type as part of itself. )比如:链表。下面就使用 Box 创建一个链表:

enum List {Cons(i32, Box<List>),Nil
}fn main() {use List::Cons;use List::Nil;let list = Cons(3, Box::new(Cons(4, Box::new(Cons(5, Box::new(Nil))))));
}

如果不使用 Box,

enum List {Cons(i32, List),Nil
}

这在编译的时候是无法确定它所需要分配的空间大小,因为 List 的大小是一个递归,也就会无限循环下去。

Rc<T>

Rc is the reference counted smart pointer. 也就是说 Rc 是带有引用计数的智能指针,适用于一个值有多个所有者。比如下面这种情况,链表 b 和链表 c 同时拥有链表 a :

use std::rc::Rc;enum List {Cons(i32, Rc<List>),Nil,
}fn main() {use List::{Cons, Nil};let a = Cons(1, Rc::new(Cons(2, Rc::new(Nil))));let tmp_a = Rc::new(a);println!("{}", Rc::strong_count(&tmp_a));let b = Cons(3, Rc::clone(&tmp_a));println!("{}", Rc::strong_count(&tmp_a));{let c = Cons(4, Rc::clone(&tmp_a));println!("{}", Rc::strong_count(&tmp_a));}println!("{}", Rc::strong_count(&tmp_a));
}

RefCell<T>

Rust 不允许不可变引用去改变数据的值,但是使用 RefCell 可以绕过编译时的检查,在运行时再进行 borrowing rules 的检查。

let a = RefCell::new(5);
println!("{}", a.borrow());
*a.borrow_mut() += 5;
println!("{}", a.borrow());

a 就是一个不可变引用,但是通过 *a.borrow_mut() 可以改变其拥有的值。
使用 RefCell 改造前面链表的例子, 使这个链表可以改变节点的值:

use std::rc::Rc;
use std::cell::RefCell;
use List::Cons;
use List::Nil;#[derive(Debug)]
enum List {Cons(Rc<RefCell<i32>>, Rc<List>),Nil,
}fn main() {let value = Rc::new(RefCell::new(1));let a = Rc::new(Cons(value.clone(), Rc::new(Nil)));let b = Cons(Rc::new(RefCell::new(3)), a.clone());let c = Cons(Rc::new(RefCell::new(4)), a.clone());println!("{:?}", a);println!("{:?}", b);println!("{:?}", c);*value.borrow_mut() += 10;println!("{:?}", a);println!("{:?}", b);println!("{:?}", c);
}

Deref && Drop Trait

Deref Trait

Implementing the Deref trait allows you to customize the behavior of the dereference operator, *(as opposed to &).
下面例子说明什么是解引用:

fn main() {let x = 5;let y = &x; // 引用assert_eq!(5, x);assert_eq!(5, *y); // 解引用
}

自己实现 Deref Trait

use std::ops::Deref;impl<T> Deref for MyBox<T> {type Target = T;fn deref(&self) -> &T {&self.0}
}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);
}

Implicit Deref Coercions with Functions and Methods

隐式解引用强制转换,这个概念通过下面的例子进行讲解:

use std::ops::Deref;impl<T> Deref for MyBox<T> {type Target = T;fn deref(&self) -> &T {&self.0}
}struct MyBox<T>(T);impl<T> MyBox<T> {fn new(x: T) -> MyBox<T> {MyBox(x)}
}fn hello(name: &str) {println!("Hello, {}!", name);
}fn main() {let m = MyBox::new(String::from("Rust"));hello(&m);
}

hello()的参数是 &str 类型,但是我们传入的是 &MyBox() 类型,&MyBox()会强制转换成&str,因为MyBox实现了Deref trait。Rust can turn &MyBox<String> into &String by calling deref. The standard library provides an implementation of Deref on String that returns a string slice.
Rust does deref coercion when it finds types and trait implementations in three cases:

  • From &T to &U when T: Deref<Target=U>
  • From &mut T to &mut U when T: DerefMut<Target=U>
  • From &mut T to &U when T: Deref<Target=U>

Drop Trait

实现Drop trait 可以自定义数据释放的时候的行为。

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.");
}

使用 drop(c) 可以进行提前释放。

Weak Reference

Problem

Rust 语言虽然确保内存安全,但是内存泄露不在它保证的范围内。比如循环引用会导致内存泄露:

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());
}

b 被 drop 的时候,分配给 b 的空间并不会被释放,因为这时候 a 是持有 b 的引用,所以 b 的引用计数为1,它分配的空间不会被清理。

Solution

通过使用 Weak<T> 解决这个问题,Weak 也是在 std::rc 这个库中,是 Rc 的降级。只要强引用计数为0,不管弱引用计数为几,堆上的空间都会被释放。

use std::rc::{Rc, Weak};
use std::cell::RefCell;#[derive(Debug)]
enum List {Cons(i32, RefCell<Weak<List>>),Nil,
}impl List {fn tail(&self) -> Option<&RefCell<Weak<List>>> {match self {Cons(_, x) => Some(x),Nil => None,}}
}
use List::{Cons, Nil};fn main() {let a = Rc::new(Cons(1, RefCell::new(Weak::new())));let b = Rc::new(Cons(2, RefCell::new(Weak::new())));if let Some(x) = List::tail(&b) {*x.borrow_mut() = Rc::downgrade(&a);}if let Some(x) = a.tail() {*x.borrow_mut() = Rc::downgrade(&b);}
}

Rust 学习笔记——智能指针相关推荐

  1. C++学习笔记——智能指针

    c++里不像java等语言设置了垃圾回收的功能,但是c++通过它的王牌指针实现了智能指针这一解决办法. 目录 异常 1.异常 2.异常的使用 3.异常的规则 4.异常安全 智能指针 概念 原理 aut ...

  2. Boost学习笔记-智能指针

    1.  智能指针 scoped_ptr 只在作用域内生效,离开作用域既释放资源,不能复制和赋值.类似于标准库的auto_ptr,但它相对于auto_ptr的优势在于,他的要求更严格,使用起来更安全.a ...

  3. 梓益C语言学习笔记之指针

    梓益C语言学习笔记之指针 一.32位平台下,地址是32位,所以指针变量占32位,共4个字节 二.内存单元的地址即为指针,存放指针的变量称为指针变量,故:"指针"是指地址,是常量,& ...

  4. rust学习笔记中级篇1–泛型(霜之小刀)

    rust学习笔记中级篇1–泛型(霜之小刀) 欢迎转载和引用,若有问题请联系 若有疑问,请联系 Email : lihn1011@163.com QQ:2279557541 结构体泛型 首先上代码,如何 ...

  5. Rust学习笔记(13)——struct、Option和Box组合应用实现单向链表

    前面深入学习了struct.Option和Box的所有权约束及各种场景的使用,现在用struct.Option和Box组合来实现一个单向链表,并实现链表常见的头部插入.删除,尾部插入.删除,中间插入. ...

  6. C++学习笔记7[指针]

    C++学习目录链接: C++学习笔记目录链接(持续更新中) 文章目录 一.变量和指针 1.指针的声明 2.指针的赋值 3.关于指针使用的说明 4.指针运算符和取地址运算符 5.指针运算 二.指针和数组 ...

  7. Rust学习笔记(9)——Option的几个方法及所有权问题

    在rust的设计中,Option的设计非常巧妙,避免了其它语言中常见的Null引起的各种错误和异常.但Option与所有权的结合,尤其是在一些特定的数据结构,如链表.图等设计中,加上引用的各种约束,就 ...

  8. 深入学习c++--智能指针(三) unique_ptr

    1. 几种智能指针 1. auto_ptr: c++11中推荐不使用他(放弃) 2. shared_ptr: 拥有共享对象所有权语义的智能指针 3. unique_ptr: 拥有独有对象所有权语义的智 ...

  9. 深入学习c++--智能指针(二) weak_ptr(打破shared_ptr循环引用)

    1. 几种智能指针 1. auto_ptr: c++11中推荐不使用他(放弃) 2. shared_ptr: 拥有共享对象所有权语义的智能指针 3. unique_ptr: 拥有独有对象所有权语义的智 ...

最新文章

  1. 最完整代码的用php备份mysql数据库
  2. 统一建模语言UML整理之开篇
  3. 基于Java多线程操作文件案例分享
  4. 从刘备面试诸葛亮看信息系统项目管理师
  5. 【逐帧分析】《黑神话:悟空》gameplay相关的技术和调整细节整理
  6. Elasticsearch-搜索并获取数据
  7. 谱比法计算岩石的品质因子的c语言程序,地层品质因子计算方法及系统与流程...
  8. 迅雷/快车/旋风地址转换器
  9. 智能一代云平台(三十六):项目中如何做到避免传递依赖
  10. mysql中日期相减_MySQL环境配置和10分钟快速入门
  11. POJ3275 Ranking the Cows【关系闭包】
  12. 简单算法系列之完数的计算
  13. 拓端tecdat|R语言样条曲线、分段线性回归模型piecewise regression估计个股beta值分析收益率数据
  14. SVN配置–服务器端(linux)
  15. Coffice协同办公管理系统(C#)(
  16. database is locked错误
  17. matlab专区--------------matlab里面如何保留小数特定位数
  18. diy一个android手机版下载,居然设计家DIY手机版下载-居然设计家DIY 安卓版v1.3.0.5-PC6安卓网...
  19. 从键盘上输入一个数,判断是否为素数。
  20. 企业微信社群该如何引流

热门文章

  1. IDEA----将本地svn项目导入idea后没有拉取提交按钮
  2. traceroute显示*号_traceroute 的名词解释
  3. pythonweb快速开发平台_30分钟快速搭建Web CRUD的管理平台--django神奇魔法
  4. Linux安装RabbitMQ及问题
  5. java读文件指定行开始到文件的最后
  6. linux下kafka安装与配置
  7. C++中对字符串的分割方法
  8. JS将Date加八小时
  9. easyui181版本使用记录
  10. Android内存泄漏检测利器:LeakCanary