rust智能指针的具体使用

 更新时间:2023年12月07日 15:00:27   作者:int8  
智能指针是一些数据结构,它们的行为类似于指针但拥有额外的元数据和附加功能,本文就来介绍一下rust智能指针的具体使用,感兴趣的可以了解一下

一、智能指针是什么

指针是一个存储内存地址的变量。这个地址指向一些其他数据。
智能指针是一类数据结构,它们类似指针,但是拥有额外的功能。智能指针的概念起源于C++。Rust标准库提供了许多智能指针,比如String和Vec<T>,虽然我们并不这么称呼它们,但这些类型都属于智能指针。
智能指针通常使用结构体实现。智能指针与常规结构体的区别在于智能指针实现了Deref和Drop trait。Deref trait使智能指针表现的像引用一样,这样就可以编写既用于引用、又用于智能指针的代码。Drop trait允许我们自定义智能指针离开作用域时的行为。

在Rust中,引用和智能指针的一个区别是引用是一类只借用数据的指针;智能指针则拥有数据的所有权。

二、最常用的一些智能指针

1.Box<T>,用于在堆上分配
2.Rc<T>,一个引用计数类型,其数据可以有多个所有者
3.Ref<T> 和RefMut<T>,通过RefCell<T> 访问(RefCell<T>是一个在运行时而不是在编译时执行借用规则的类型)

(一)Box指针

Box<T>类型是一个智能指针,因为它实现了Deref trait和Drop trait。
box把值放在堆上而不是栈上。留在栈上的则是指向堆数据的指针。除了数据被储存在堆上而不是栈上之外,box没有性能损失。不过也没有很多额外的功能。
1.创建Box
使用new函数创建
例子

fn main() {
     let var_i32 = 5; // 默认数据保存在 栈 上
     let b = Box::new(var_i32); // 使用Box后数据会存储在堆上
     println!("b = {}", b);
}

b离开作用域时,它将自动释放。这个释放包括b本身(位于栈上)和它所指向的数据(位于堆上)。

2.使用box
像使用引用一样使用box。
使用解引用操作符 * 解引用box

fn main() {
     let x = 5; // 值类型数据
     let y = Box::new(x); // y是一个智能指针,指向堆上存储的数据5
     println!("{}",5==x);
     println!("{}",5==*y); // 为了访问y存储的具体数据,需要解引用
}
编译运行结果如下
true
true
直接使用  5 == y  会返回false

3.使用Box创建递归类型
Rust需要在编译时知道类型占用多少空间。一种无法在编译时知道大小的类型是递归类型,其值的一部分可以是自身类型的另一个值。这种嵌套可以是无限的,所以Rust不知道递归类型需要多少空间。不过box有一个已知的大小,所以通过在递归类型定义中插入box,就可以创建递归类型了。一个常见递归类型就是链表。
实例

enum List {
     Cons(i32, List),
     Nil,
}
use crate::List::{Cons, Nil};
fn main() {
     let list = Cons(1, Cons(2, Cons(3, Nil)));//使用这个list来储存1, 2, 3
}

第一个Cons储存1和另一个List值。这个List是一个Cons值,此cons储存2和下一个List值。这个list又是一个cons,储存3和值为Nil的List。这段代码编译错误。因为这个类型 “有无限的大小”。
因为Box<T> 是一个指针,它的大小是确定的,所以将Box作为Cons的成员,这样List的大小就确定了。

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

三、两个特性

(一)Deref Trait

1.Deref是由Rust标准库提供的一个特性。
实现Deref之后就能把智能指针当作引用使用,相当于重载解引用运算符*。
Deref中包含deref()方法。
deref()方法用于引用self实例并返回一个指向内部数据的指针。

例子

use std::ops::Deref;
struct DerefExample<T> {
     value: T
}
impl<T> Deref for DerefExample<T> {
     type Target = T;
     fn deref(&self) -> &Self::Target {
         &self.value
     }
}
let x = DerefExample { value: 'a' };
assert_eq!('a', *x);

范例

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); // 调用new() 返回创建一个结构体实例
     println!("5==x is {}",5==x);
     println!("5==*y is {}",5==*y); // 解引用y
     println!("x==*y is {}",x==*y); // 解引用y
}
编译运行结果如下
5==x is true
5==*y is true
x==*y is true

每次使用 * 时, * 运算符都被替换成先调用deref方法再使用 * 解引用的操作,且只会发生一次,不会无限递归替换 * 操作符,解引用出i32类型的值就停止了

2.DerefMut trait用于重载可变引用的 * 运算符

3.Deref隐式转换
Deref隐式转换将实现了Deref的类型的引用转换为另一种类型的引用。例如,将&String转换为&str,因为String实现了Deref因此可以返回&str。Deref强制转换是Rust在函数或方法传参上的一种便利操作,并且只能作用于实现了Deref的类型。当这种特定类型的引用作为实参传递给和形参类型不同的函数时将自动转换类型。这时会有一系列的deref方法被调用,把我们提供的类型转换成了形参所需的类型。
Deref隐式转换使Rust程序员在调用函数时无需使用过多& 和 *。这个功能方便我们编写同时作用于引用或智能指针的代码。

实例

//还是上面的MyBox<T>
fn hello(name: &str) {
     println!("Hello, {name}!");
}
let m = MyBox::new(String::from("Rust"));
hello(&m);

因为Deref隐式转换,使用MyBox<String>的引用作为参数是可行的。
因为MyBox<T>实现了Deref,Rust可以通过deref将&MyBox<String>变为&String。而String也实现了Deref,Rust再次调用deref将&String变为&str,这就符合hello函数的定义了。

如果没有Deref强制转换,要把&MyBox<String>类型的值传给hello函数,则不得不编写如下代码

let m = MyBox::new(String::from("Rust"));
hello(&(*m)[..]);

(*m)MyBox<String>解引用为String,接着&和[..]将String转换成&str。
没有Deref强制转换的话,所有这些符号混在一起将难以读写和理解。Deref强制转换会自动执行这些转换。这些转换发生在编译时,所以没有运行时损耗!

Deref隐式转换有三种情形:
(1)当T实现Deref Target=U 时从 &T到 &U。
(2)当T实现DerefMut Target=U 时从 &mut T到 &mut U。
(3)当T实现Deref Target=U 时从 &mut T到 &U。
第一种情况表明如果有一个 &T,而T实现了返回U类型的Deref,则可以直接得到 &U。
第二种情况表明可变引用也有着相同的行为。
第三个情况将可变引用强转为不可变引用。但是反过来是不行的,不可变引用永远也不能强转为可变引用。
这三种情况下,T类型都自动实现了U类型的所有方法。

(二)Drop Trait

Rust中的析构函数是由Drop trait提供的drop()方法。
Drop Trait只有一个方法drop() 。
实现了Drop特质的结构体在离开了它的作用域时会调用drop()方法。
例子

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
     }
}
impl<T> Drop for MyBox<T>{
     fn drop(&mut self){
         println!("dropping MyBox object from memory ");
     }
}
fn main() {
     let x = 50;
     MyBox::new(x);
     MyBox::new("Hello");
}
编译运行结果如下
dropping MyBox object from memory
dropping MyBox object from memory

到此这篇关于rust智能指针的具体使用的文章就介绍到这了,更多相关rust智能指针内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • rust多个mod文件引用和文件夹mod使用注意事项小结

    rust多个mod文件引用和文件夹mod使用注意事项小结

    在 Rust 项目中,可以使用 mod 关键字将一个文件夹或一个 rs 文件作为一个模块引入到当前文件中,本文给大家介绍rust多个mod文件引用和文件夹mod使用注意事项小结,感兴趣的朋友跟随小编一起看看吧
    2024-03-03
  • Rust 语言中的 into() 方法及代码实例

    Rust 语言中的 into() 方法及代码实例

    在 Rust 中,into() 方法通常用于将一个类型的值转换为另一个类型,这通常涉及到资源的所有权转移,本文给大家介绍Rust 语言中的 into() 方法及代码实例,感谢的朋友跟随小编一起看看吧
    2024-03-03
  • Rust突破编译器限制构造可修改的全局变量

    Rust突破编译器限制构造可修改的全局变量

    这篇文章主要为大家介绍了Rust突破编译器限制构造可修改的全局变量示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • Rust标量类型的具体使用

    Rust标量类型的具体使用

    本文主要介绍了Rust标量类型的具体使用,其中包括整数类型、浮点类型、布尔类型以及字符类型,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • 详解Rust Substrate框架中的Runtime

    详解Rust Substrate框架中的Runtime

    ubstrate是一个区块链开发框架,它提供了一系列模块化和可扩展的组件,可以帮助开发人员快速构建自定义区块链。 Runtime是Substrate区块链的核心部分,文中有详细的代码示例,需要的朋友可以参考下
    2023-05-05
  • Rust 多线程编程的实现

    Rust 多线程编程的实现

    在rust中,多线程编程不算困难,但是也需要留心和别的编程语言中不同的地方,本文主要介绍了Rust 多线程编程的实现,感兴趣的可以了解一下
    2023-12-12
  • Rust日期与时间的操作方法

    Rust日期与时间的操作方法

    Rust的时间操作主要用到chrono库,接下来我将简单选一些常用的操作进行介绍,感兴趣的朋友跟随小编一起看看吧
    2023-09-09
  • Rust语言实现图像编码转换

    Rust语言实现图像编码转换

    image-rs库是 Rust 社区中广泛使用的一个开源库,它提供了丰富的图像编解码功能,本文主要介绍了Rust语言实现图像编码转换,具有一定的参考价值,感兴趣的可以了解一下
    2024-05-05
  • 使用win10 wsl子系统如何将 rust 程序静态编译为linux可执行文件

    使用win10 wsl子系统如何将 rust 程序静态编译为linux可执行文件

    这篇文章主要介绍了使用win10 wsl子系统如何将 rust 程序静态编译为linux可执行文件,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2025-05-05
  • Rust 标准库的结构及模块路径详解

    Rust 标准库的结构及模块路径详解

    在 Rust 中,标准库提供了一组核心功能,以帮助开发者执行常见的编程任务,这个路径树可以作为参考,帮助你更好地理解 Rust 标准库的结构和模块之间的关系,本文介绍 Rust 标准库的结构,并提供相应的 use 路径,感兴趣的朋友一起看看吧
    2024-05-05

最新评论