开发人员在管理堆或堆栈上的数据时可以使用传统的指针方法。 但是,使用这些指针方法也有缺点,例如当动态分配的对象没有及时进行垃圾回收时会导致内存泄漏。 好消息是存在更好的内存管理方法,可以自动处理垃圾收集而无需运行时成本,它们被称为智能指针。
Rust 是一种开源、低级、面向对象和静态类型的编程语言,具有高效的内存管理,可确保高性能和安全性。 它具有许多组织用来构建高度安全和强大的应用程序的广泛功能,包括 Web、移动、游戏和网络应用程序。
本文将让您了解什么是智能指针、它们的用例以及它们在 Rust 中的实现。 包含的代码示例将向您介绍 Rust 的各种类型的智能指针。
向前跳:
什么是智能指针?
智能指针如何在 Rust 中工作
Deref特征
Drop特征
Rust 中的智能指针类型及其用例
这 Rc智能指针
这 Box智能指针
这 RefCell智能指针
智能指针是 抽象数据类型 ,在编程中类似于常规 指针 (存储值的内存地址的变量),并具有析构函数和重载运算符等附加功能。 它们还包括自动内存管理,以解决内存泄漏等问题。
当开发人员将包含动态分配数据的内存与智能指针链接时,它们会被自动取消分配或清理。
一些智能指针用例包括:
自动解除分配数据和销毁对象
检查超出范围的数据或变量
减少与使用常规指针相关的错误
在取消分配数据后保持程序的效率
跟踪程序数据/对象/变量的所有内存地址
在程序应用程序中管理网络连接
的系统(或一组规则)实现内存管理, Rust 通过称为所有权 所有权包含在应用程序的程序中,并在程序成功编译之前由编译器进行检查,而不会导致任何停机。
通过使用 structs ,Rust 执行智能指针。 在前面提到的智能指针的附加功能中,它们还具有拥有值本身的能力。
接下来,您将了解一些有助于在 Rust 中自定义智能指针操作的特征。
超过 20 万开发人员使用 LogRocket 来创造更好的数字体验 了解更多 →
这 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特质类似于 Dereftrait 但用于解构,Rust 通过清理程序不再使用的资源自动实现。 所以 Droptrait 用于存储未使用值的指针,然后释放该值占用的内存空间。
要使用 Drop特质,你需要实现 drop()具有可变引用的方法,该引用对不再需要或超出范围的值执行销毁,定义为:
fn drop(&mut self) {};
为了更好地了解如何 Drop特质有效,请参见以下示例:
不要错过 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 中存在几种类型的智能指针。 在本节中,您将通过代码示例了解其中一些类型及其用例。 他们包括:
Rc
Box
RefCell
这 Rc
顾名思义,引用计数智能指针类型记录了代码中每个变量的引用数。 当引用计数返回零时,它们不再使用,智能指针将清除它们。
在以下示例中,您将创建与一个列表共享所有权的三个列表。 第一个列表将有两个值,第二个和第三个列表将第一个列表作为它们的第二个值。 这意味着最后两个列表将与第一个列表共享所有权。 您将首先包括 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)); }
运行代码后,结果如下:
在 Rust 中,数据分配通常在堆栈中完成。 但是,Rust 中的某些方法和智能指针类型使您能够在堆中分配数据。 其中一种类型是 Box智能指针 ; “
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
将具有值的变量绑定到另一个变量并使用第二个变量将在 Rust 中产生错误。 Rust 中的 所有权规则 确保每个值都有一个所有者。 在绑定所有权被移动后,您不能使用绑定,因为 Rust 会为每个绑定创建一个引用,除非使用 Copy特质 。
Rust 中的借用规则需要借用所有权作为引用,您可以拥有一个/多个引用 ( &T) 到资源或一个可变引用 ( &mut T).
但是,Rust 中称为“内部可变性”的设计模式允许您使用不可变引用来改变这些数据。 RefCell
和 RefCell
此前,在 Rc
#[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
创建 _first_list共享所有权为 data
创建另外两个列表, _second_list和 _third_list, 与 _first_list
调用 borrow_mut()函数(返回 RefMut
请注意,如果您不包括 #[derive(Debug)]作为代码的第一行,您将遇到以下错误:
Once the code runs, the values of the first list, second list, and third list will be mutated:
您已经到了本文的结尾,在那里您了解了智能指针,包括它们的用例。 我们介绍了智能指针在 Rust 中的工作方式及其特征( Deref特质和 Drop特征)。 您还了解了 Rust 中的一些智能指针类型和用例,包括 Rc
调试 Rust 应用程序可能很困难,尤其是当用户遇到难以重现的问题时。 如果您对监控和跟踪 Rust 应用程序的性能、自动显示错误以及跟踪缓慢的网络请求和加载时间感兴趣,请 尝试 LogRocket 。
LogRocket 就像一个用于 Web 和移动应用程序的 DVR,几乎可以记录 Rust 应用程序上发生的所有事情。 无需猜测问题发生的原因,您可以汇总并报告问题发生时应用程序所处的状态。 LogRocket 还监控您的应用程序的性能,报告客户端 CPU 负载、客户端内存使用情况等指标。