理解 Rust 中的智能指针

开发人员在管理堆或堆栈上的数据时可以使用传统的指针方法。 但是,使用这些指针方法也有缺点,例如当动态分配的对象没有及时进行垃圾回收时会导致内存泄漏。 好消息是存在更好的内存管理方法,可以自动处理垃圾收集而无需运行时成本,它们被称为智能指针。

Rust 是一种开源、低级、面向对象和静态类型的编程语言,具有高效的内存管理,可确保高性能和安全性。 它具有许多组织用来构建高度安全和强大的应用程序的广泛功能,包括 Web、移动、游戏和网络应用程序。

本文将让您了解什么是智能指针、它们的用例以及它们在 Rust 中的实现。 包含的代码示例将向您介绍 Rust 的各种类型的智能指针。

向前跳:

  • 什么是智能指针?

  • 智能指针如何在 Rust 中工作

  • Deref特征

  • Drop特征

  • Rust 中的智能指针类型及其用例

    • 这 Rc智能指针

    • 这 Box智能指针

    • 这 RefCell智能指针

什么是智能指针?

智能指针是 抽象数据类型 ,在编程中类似于常规 指针 (存储值的内存地址的变量),并具有析构函数和重载运算符等附加功能。 它们还包括自动内存管理,以解决内存泄漏等问题。

当开发人员将包含动态分配数据的内存与智能指针链接时,它们会被自动取消分配或清理。

一些智能指针用例包括:

  • 自动解除分配数据和销毁对象

  • 检查超出范围的数据或变量

  • 减少与使用常规指针相关的错误

  • 在取消分配数据后保持程序的效率

  • 跟踪程序数据/对象/变量的所有内存地址

  • 在程序应用程序中管理网络连接

智能指针如何在 Rust 中工作

的系统(或一组规则)实现内存管理, Rust 通过称为所有权 所有权包含在应用程序的程序中,并在程序成功编译之前由编译器进行检查,而不会导致任何停机。

通过使用 structs ,Rust 执行智能指针。 在前面提到的智能指针的附加功能中,它们还具有拥有值本身的能力。

接下来,您将了解一些有助于在 Rust 中自定义智能指针操作的特征。


超过 20 万开发人员使用 LogRocket 来创造更好的数字体验 了解更多 →


Deref特征

这 Dereftrait 用于有效的解引用,可以轻松访问存储在智能指针后面的数据。 您可以使用 Deref将智能指针视为引用的特征。

取消引用运算符意味着使用一元运算符 *作为从具有一元引用运算符的指针派生的内存地址的前缀 &标记为“参考”。 表达式可以是可变的 ( &mut) 或不可变的 ( *mut)。 在内存地址上使用解引用运算符从指针点返回值的位置。

因此, Dereftrait 只是自定义解引用操作符的行为。

下面是一个插图 Deref特征:

fn main() {
        let first_data = 20;
        let second_data = &first_data;
        
        if first_data == *second_data {
                println!("The values are equal");
      } else {
             println!("The values are not equal");
     }
}

上面代码块中的函数实现了以下功能:

  • 存储的值 20在一个 first_data多变的

  • 这 second_data变量使用引用运算符 &存储的内存地址 first_data多变的

  • 检查的值是否的条件 first_data等于的值 second_data. 解引用运算符 *用于 second_data获取存储在指针内存地址中的值

下面的屏幕截图显示了代码的输出:

Drop特征

这 Drop特质类似于 Dereftrait 但用于解构,Rust 通过清理程序不再使用的资源自动实现。 所以 Droptrait 用于存储未使用值的指针,然后释放该值占用的内存空间。

要使用 Drop特质,你需要实现 drop()具有可变引用的方法,该引用对不再需要或超出范围的值执行销毁,定义为:

fn drop(&mut self) {};

为了更好地了解如何 Drop特质有效,请参见以下示例:


来自 LogRocket 的更多精彩文章:

  • 不要错过 The Replay 来自 LogRocket 的精选时事通讯

  • 了解 LogRocket 的 Galileo 如何消除噪音以主动解决应用程序中的问题

  • 使用 React 的 useEffect 优化应用程序的性能

  • 之间切换 在多个 Node 版本

  • 了解如何 使用 AnimXYZ 为您的 React 应用程序制作动画

  • 探索 Tauri ,一个用于构建二进制文件的新框架

  • 比较 NestJS 与 Express.js


struct Consensus  {
        small_town: i32
}
​
impl Drop for Consensus {
        fn drop(&mut self) {
                println!("This instance of Consensus has being dropped: {}", self.small_town);
}
}
​
fn main() {
        let _first_instance = Consensus{small_town: 10};
        let _second_instance = Consensus{small_town: 8};
​
        println!("Created instances of Consensus");
}
The code above implements the following:
  • 一个包含 32 位有符号整数类型值的结构体,称为 small_town被建造

  • 这 Drop特征包含 drop()具有可变引用的方法是用 impl结构上的关键字。 内的消息 println!当 main()函数超出范围(即,当 main()函数完成运行)

  • 这 main()函数只是创建两个实例 Consensus并在 println!创建后显示在屏幕上

下面的屏幕截图显示了代码的输出:

Rust 中的智能指针类型及其用例

Rust 中存在几种类型的智能指针。 在本节中,您将通过代码示例了解其中一些类型及其用例。 他们包括:

  • Rc

  • Box

  • RefCell

这 Rc智能指针

这 Rc代表引用计数的智能指针类型。 在 Rust 中,每个值每次都有一个所有者,并且 是违反所有权规则 一个值拥有多个所有者 的。 但是,当您声明一个值并在代码中的多个位置使用它时,引用计数类型允许您为变量创建多个引用。

顾名思义,引用计数智能指针类型记录了代码中每个变量的引用数。 当引用计数返回零时,它们不再使用,智能指针将清除它们。

在以下示例中,您将创建与一个列表共享所有权的三个列表。 第一个列表将有两个值,第二个和第三个列表将第一个列表作为它们的第二个值。 这意味着最后两个列表将与第一个列表共享所有权。 您将首先包括 Rc前奏 与 use语句,这将允许您访问所有可在您的代码中使用的 RC 方法。

然后你会:

  • 定义一个列表 enum关键字和 List{}

  • 创建一对构造 Cons()保存引用计数值列表

  • 声明另一个 use定义列表的语句

  • 创建一个 main 函数来实现以下功能:

    • 构造一个新的引用计数列表作为第一个列表

    • 通过将第一个列表的引用作为参数传递来创建第二个列表。 使用 clone()函数,它创建一个新指针,该指针指向第一个列表中的值的分配

    • 通过调用 Rc::strong_count()功能

在您喜欢的代码编辑器中键入以下代码:

use std::rc::Rc;
 
enum List {
   Cons(i32, Rc), Nil,
}
 
use List::{Cons, Nil};
 
fn main() {
   let _first_list = Rc::new(Cons(10, Rc::new(Cons(20, Rc::new(Nil)))));
   println!("The count after creating _first_list is {}", Rc::strong_count(&_first_list));
   let _second_list = Cons(8, Rc::clone(&_first_list));
   println!("The count after creating _second_list is {}", Rc::strong_count(&_first_list));
   { 
       let _third_list = Cons(9, Rc::clone(&_first_list));
       println!("The count after creating _third_list is {}", Rc::strong_count(&_first_list));
   }
 
   println!("The count after _third_list goes out of scope is {}", Rc::strong_count(&_first_list));
}

运行代码后,结果如下:

这 Box智能指针

在 Rust 中,数据分配通常在堆栈中完成。 但是,Rust 中的某些方法和智能指针类型使您能够在堆中分配数据。 其中一种类型是 Box智能指针 ; “”代表数据类型。 要使用 Box 智能指针将值存储在堆中,您可以包装以下代码: Box::new()周围。 例如,假设您在堆中存储一个值:

fn main() {
        let stack_data = 20;
        let hp_data = Box::new(stack_data); // points to the data in the heap
        println!("hp_data = {}", hp_data);  // output will be 20.
}

从上面的代码块中,请注意:

  • 价值 stack_data存储在堆中

  • Box 智能指针 hp_data存储在堆栈中

此外,您可以通过使用前面的星号 (*) 轻松取消引用存储在堆中的数据 hp_data. 代码的输出将是:

这 RefCell智能指针

RefCell是一种在运行时而不是在编译时执行借用规则的智能指针类型。 在编译时,Rust 中的开发人员可能会遇到“借用检查器”的问题,由于不遵守 Rust 的所有权规则,他们的代码仍未编译。

将具有值的变量绑定到另一个变量并使用第二个变量将在 Rust 中产生错误。 Rust 中的 所有权规则 确保每个值都有一个所有者。 在绑定所有权被移动后,您不能使用绑定,因为 Rust 会为每个绑定创建一个引用,除非使用 Copy特质 。

Rust 中的借用规则需要借用所有权作为引用,您可以拥有一个/多个引用 ( &T) 到资源或一个可变引用 ( &mut T).

但是,Rust 中称为“内部可变性”的设计模式允许您使用不可变引用来改变这些数据。 RefCell使用这种“内部可变性”设计模式和数据中的 不安全代码 ,并在运行时强制执行借用规则。

和 RefCell,可以在运行时检查可变和不可变借用。 因此,如果您的代码中有包含多个不可变引用的数据,请使用 RefCell,您仍然可以改变数据。

此前,在 Rc部分,您使用了一个实现多个共享所有权的示例。 在下面的示例中,您将修改 Rc通过包装的代码示例 Rc大约 RefCell当定义 Cons:

#[derive(Debug)]
enum List {
  Cons(Rc>, Rc), Nil,
}
use List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
   let data = Rc::new(RefCell::new(10));
 
   let _first_list = Rc::new(Cons(Rc::clone(&data), Rc::new(Nil)));
 
   let _second_list = Cons(Rc::new(RefCell::new(9)), Rc::clone(&_first_list));
 
   let _third_list = Cons(Rc::new(RefCell::new(10)), Rc::clone(&_first_list));
 
   *data.borrow_mut() += 20;
 
   println!("first list after = {:?}", _first_list);
   println!("second list after = {:?}", _second_list);
   println!("third list after = {:?}", _third_list);
}

上面的代码实现了以下功能:

  • 创建 data和 Rc>中定义的 Cons

  • 创建 _first_list共享所有权为 data

  • 创建另外两个列表, _second_list和 _third_list, 与 _first_list

  • 调用 borrow_mut()函数(返回 RefMut智能指针)在数据上并使用解引用运算符 *取消引用 Rc,从RefCell中获取内部值,并对值进行变异

请注意,如果您不包括 #[derive(Debug)]作为代码的第一行,您将遇到以下错误:

Once the code runs, the values of the first list, second list, and third list will be mutated:

改图鸭(gaituya.com),图片在线编辑网站,30多个功能免费使用!

结论

您已经到了本文的结尾,在那里您了解了智能指针,包括它们的用例。 我们介绍了智能指针在 Rust 中的工作方式及其特征( Deref特质和 Drop特征)。 您还了解了 Rust 中的一些智能指针类型和用例,包括 Rc, Box, 和 RefCell.

LogRocket :全面了解生产 Rust 应用程序

调试 Rust 应用程序可能很困难,尤其是当用户遇到难以重现的问题时。 如果您对监控和跟踪 Rust 应用程序的性能、自动显示错误以及跟踪缓慢的网络请求和加载时间感兴趣,请 尝试 LogRocket 。

LogRocket 就像一个用于 Web 和移动应用程序的 DVR,几乎可以记录 Rust 应用程序上发生的所有事情。 无需猜测问题发生的原因,您可以汇总并报告问题发生时应用程序所处的状态。 LogRocket 还监控您的应用程序的性能,报告客户端 CPU 负载、客户端内存使用情况等指标。

你可能感兴趣的:(rust,开发语言,后端)