开发人员在管理堆或堆栈上的数据时可以使用传统的指针方法。 但是,使用这些指针方法也有缺点,例如当动态分配的对象没有及时进行垃圾回收时会导致内存泄漏。 好消息是存在更好的内存管理方法,可以自动处理垃圾收集而无需运行时成本,它们被称为智能指针。
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<T>
-
Box<T>
-
RefCell<T>
这 Rc<T>智能指针
这 Rc<T>代表引用计数的智能指针类型。 在 Rust 中,每个值每次都有一个所有者,并且 是违反所有权规则 一个值拥有多个所有者 的。 但是,当您声明一个值并在代码中的多个位置使用它时,引用计数类型允许您为变量创建多个引用。
顾名思义,引用计数智能指针类型记录了代码中每个变量的引用数。 当引用计数返回零时,它们不再使用,智能指针将清除它们。
在以下示例中,您将创建与一个列表共享所有权的三个列表。 第一个列表将有两个值,第二个和第三个列表将第一个列表作为它们的第二个值。 这意味着最后两个列表将与第一个列表共享所有权。 您将首先包括 Rc前奏 与 use语句,这将允许您访问所有可在您的代码中使用的 RC 方法。
然后你会:
-
定义一个列表 enum关键字和 List{}
-
创建一对构造 Cons()保存引用计数值列表
-
声明另一个 use定义列表的语句
-
创建一个 main 函数来实现以下功能:
-
构造一个新的引用计数列表作为第一个列表
-
通过将第一个列表的引用作为参数传递来创建第二个列表。 使用 clone()函数,它创建一个新指针,该指针指向第一个列表中的值的分配
-
通过调用 Rc::strong_count()功能
-
在您喜欢的代码编辑器中键入以下代码:
use std::rc::Rc; enum List { Cons(i32, Rc<List>), 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<T>智能指针
在 Rust 中,数据分配通常在堆栈中完成。 但是,Rust 中的某些方法和智能指针类型使您能够在堆中分配数据。 其中一种类型是 Box智能指针 ; “<T>”代表数据类型。 要使用 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<T>智能指针
RefCell<T>是一种在运行时而不是在编译时执行借用规则的智能指针类型。 在编译时,Rust 中的开发人员可能会遇到“借用检查器”的问题,由于不遵守 Rust 的所有权规则,他们的代码仍未编译。
将具有值的变量绑定到另一个变量并使用第二个变量将在 Rust 中产生错误。 Rust 中的 所有权规则 确保每个值都有一个所有者。 在绑定所有权被移动后,您不能使用绑定,因为 Rust 会为每个绑定创建一个引用,除非使用 Copy特质 。
Rust 中的借用规则需要借用所有权作为引用,您可以拥有一个/多个引用 ( &T) 到资源或一个可变引用 ( &mut T).
但是,Rust 中称为“内部可变性”的设计模式允许您使用不可变引用来改变这些数据。 RefCell<T>使用这种“内部可变性”设计模式和数据中的 不安全代码 ,并在运行时强制执行借用规则。
和 RefCell<T>,可以在运行时检查可变和不可变借用。 因此,如果您的代码中有包含多个不可变引用的数据,请使用 RefCell<T>,您仍然可以改变数据。
此前,在 Rc<T>部分,您使用了一个实现多个共享所有权的示例。 在下面的示例中,您将修改 Rc<T>通过包装的代码示例 Rc<T>大约 RefCell<T>当定义 Cons:
#[derive(Debug)] enum List { Cons(Rc<RefCell<i32>>, Rc<List>), 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<RefCell<i32>>中定义的 Cons
-
创建 _first_list共享所有权为 data
-
创建另外两个列表, _second_list和 _third_list, 与 _first_list
-
调用 borrow_mut()函数(返回 RefMut<T>智能指针)在数据上并使用解引用运算符 *取消引用 Rc<T>,从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<T>, Box<T>, 和 RefCell<T>.
LogRocket :全面了解生产 Rust 应用程序
调试 Rust 应用程序可能很困难,尤其是当用户遇到难以重现的问题时。 如果您对监控和跟踪 Rust 应用程序的性能、自动显示错误以及跟踪缓慢的网络请求和加载时间感兴趣,请 尝试 LogRocket 。
LogRocket 就像一个用于 Web 和移动应用程序的 DVR,几乎可以记录 Rust 应用程序上发生的所有事情。 无需猜测问题发生的原因,您可以汇总并报告问题发生时应用程序所处的状态。 LogRocket 还监控您的应用程序的性能,报告客户端 CPU 负载、客户端内存使用情况等指标。