0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

CRust学习笔记:智能指针和内部可变性

jf_wN0SrCdH 来源:coding到灯火阑珊 2023-01-29 14:58 次阅读

本系列文章是Jon Gjengset发布的CRust of Rust系列视频的学习笔记,CRust of Rust是一系列持续更新的Rust中级教程

我们将通过手动实现Cell,RefCell来学习智能指针及内部可变性。

新建一个项目:

cargo new --lib pointers
手动实现Cell 在src目录下新建一个cell.rs文件,然后在lib.rs中加入:
1modcell;
在cell.rs中写入如下代码:
 1usestd::UnsafeCell;
 2
 3pubstructCell{
 4value:UnsafeCell,
 5}
 6
 7implCell{
 8pubfnnew(value:T)->Self{
 9Cell{
10value:UnsafeCell::new(value),
11}
12}
13
14pubfnset(&self,value:T){
15unsafe{
16*self.value.get()=value
17};
18}
19
20pubfnget(&self)->TwhereT:Copy{
21unsafe{
22*self.value.get()
23}
24}
25}

实现内部可变性需要一个特殊的cell类型,叫作UnsafeCell,它是Rust的核心原语。顾名思义它是不安全的,它持有类型,通过它可以获得类型的原生独占指针。我们使用UnsafeCell类型可以通过共享引用转变成独占引用来改变数据。

在set和get方法中需要获取类型的原生的指针,因此需要使用unsafe块。由于UnsafeCell实现了!Sync trait,表示不能安全的跨线程共享引用。

测试代码如下:

 1#[cfg(test)]
 2modtest{
 3usesuper::Cell;
 4
 5#[test]
 6fncell_test(){
 7letmutx=Cell::new(42);
 8leti=x.get();
 9x.set(43);
10
11assert_eq!(i,42);
12}
13}
执行cargo test,测试通过。 手动实现RefCell

RefCell的文档描述是:可以改变内存位置,可以动态检查借用规则。也就是说可以在运行期检查借用规则。

在src目录下新建一个refcell.rs文件,然后在lib.rs中加入:

1modrefcell;
在refcell.rs中写入如下代码:
 1usestd::UnsafeCell;
 2usecrate::Cell;
 3
 4#[derive(Clone,Copy)]
 5enumRefState{
 6Unshared,
 7Shared(usize),
 8Exclusive,
 9}
10
11pubstructRefCell{
12value:UnsafeCell,
13state:Cell,
14}
15
16implRefCell{
17pubfnnew(value:T)->Self{
18Self{
19value:UnsafeCell::new(value),
20state:Cell::new(RefState::Unshared),
21}
22}
23
24pubfnborrow(&self)->Option<&T>{
25None
26}
27
28pubfnborrow_mut(&self)->Option<&mut T>{
29None
30}
31}

这是RefCell 最基本的API。borrow和borrow_mut方法的返回值使用Option,是因为如果通过borrow_mut获取可变引用,则通过borrow获取的共享引用返回None,反之亦然。

RecCell的引用状态有三种,分别是非共享状态(Unshared)、共享状态(Shared)、独占状态,使用enum来表示。其中共享状态包含了一个引用计数器,类型为usize。

引用状态我们使用了上面刚完成的Cell进行包装,是因为需要使用内部可变性来改变状态。

下面来完成borrow和borrow_mut方法:

 1pubfnborrow(&self)->Option<&T>{
 2matchself.state.get(){
 3//当前状态如果是非共享状态,则设置引用状态为共享状态
 4RefState::Unshared=>{
 5self.state.set(RefState::Shared(1));
 6Some(unsafe{&*self.value.get()})
 7},
 8//当前状态如果是共享状态,则引用计数加1
 9RefState::Shared(n)=>{
10self.state.set(RefState::Shared(n+1));
11Some(unsafe{&*self.value.get()})
12},
13//当前状态如果是独占状态,则返回None
14RefState::Exclusive=>None,
15}
16}
17
18pubfnborrow_mut(&self)->Option<&mut T>{
19//引用状态既不是共享状态,也不是独占状态,才能设置为独占状态。
20ifletRefState::Unshared=self.state.get(){
21self.state.set(RefState::Exclusive);
22Some(unsafe{&mut*self.value.get()})
23}else{
24None
25}
26}
现在有个问题,共享状态的引用计数只有增没有减,下面增加两个类型来完善RefCell
 1/**
 2*包装RefCell的共享引用struct
 3*/
 4pubstructRef<'refcell, T>{
 5refcell:&'refcellRefCell,
 6}
 7
 8implDropforRef<'_, T>{
 9//超出作用域范围时,共享引用状态的变化
10fndrop(&mutself){
11matchself.refcell.state.get(){
12RefState::Unshared|RefState::Exclusive=>unreachable!(),
13RefState::Shared(1)=>{
14self.refcell.state.set(RefState::Unshared);
15},
16RefState::Shared(n)=>{
17self.refcell.state.set(RefState::Shared(n-1));
18}
19}
20}
21}
22
23implstd::DerefforRef<'_, T>{
24typeTarget=T;
25
26//解引用时直接返回T的引用
27fnderef(&self)->&Self::Target{
28unsafe{&*self.refcell.value.get()}
29}
30}
 1/**
 2*包装RefCell的可变引用struct
 3*/
 4pubstructRefMut<'refcell, T>{
 5refcell:&'refcellRefCell,
 6}
 7
 8implDropforRefMut<'_, T>{
 9//超出作用域范围时,独占引用状态的变化
10fndrop(&mutself){
11matchself.refcell.state.get(){
12RefState::Unshared|RefState::Shared(_)=>unreachable!(),
13RefState::Exclusive=>{
14self.refcell.state.set(RefState::Unshared);
15}
16}
17}
18}
19
20implstd::DerefforRefMut<'_, T>{
21typeTarget=T;
22
23//解引用时直接返回T的引用
24fnderef(&self)->&Self::Target{
25unsafe{&*self.refcell.value.get()}
26}
27}
28
29implstd::DerefMutforRefMut<'_, T>{
30//解引用时直接返回T的可变引用
31fnderef_mut(&mutself)->&mutSelf::Target{
32unsafe{&mut*self.refcell.value.get()}
33}
34}
RefCell的borrow和borrow_mut方法的返回值也需要做相应的修改:
 1pubfnborrow(&self)->Option>{
 2matchself.state.get(){
 3//当前状态如果是非共享状态,则设置引用状态为共享状态
 4RefState::Unshared=>{
 5self.state.set(RefState::Shared(1));
 6Some(Ref{refcell:self})
 7},
 8//当前状态如果是共享状态,则引用计数加1
 9RefState::Shared(n)=>{
10self.state.set(RefState::Shared(n+1));
11Some(Ref{refcell:self})
12},
13//当前状态如果是独占状态,则返回None
14RefState::Exclusive=>None,
15}
16}
17
18pubfnborrow_mut(&self)->Option>{
19//引用状态既不是共享状态,也不是独占状态,才能设置为独占状态。
20ifletRefState::Unshared=self.state.get(){
21self.state.set(RefState::Exclusive);
22Some(RefMut{refcell:self})
23}else{
24None
25}
26}
通过我们自己实现的Cell,RefCell的过程,我们了解了智能指针为什么需要实现Drop、Deref和DerefMut特征及内部可变性的原理。

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 计数器
    +关注

    关注

    32

    文章

    2123

    浏览量

    92987
  • 文件
    +关注

    关注

    1

    文章

    540

    浏览量

    24402
  • 指针
    +关注

    关注

    1

    文章

    473

    浏览量

    70363
  • 代码
    +关注

    关注

    30

    文章

    4556

    浏览量

    66784
  • Rust
    +关注

    关注

    1

    文章

    223

    浏览量

    6387

原文标题:CRust学习笔记:智能指针和内部可变性

文章出处:【微信号:Rust语言中文社区,微信公众号:Rust语言中文社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Windows -编程-变量和可变性

    Windows -编程-变量和可变性默认情况下变量是不可变的。这是 Rust 为您提供的众多推动之一,您可以利用 Rust 提供的安全性和简单的并发性来编写代码。但是,您仍然可以选择使变量可变
    发表于 08-24 14:27

    Windows -编程-变量和可变性-阴影

    Windows -编程-变量和可变性-阴影正如您 在第 2 章“比较猜测与秘密数字”部分的猜谜游戏教程中所见,您可以声明一个与前一个变量同名的新变量。Rustaceans 说第一个变量被第二个变量
    发表于 08-24 14:35

    人工智能要面对的风险

    专注于学习可变性。即加强战略与作战思维并学习技能以应对环境变化。
    的头像 发表于 06-24 09:43 1990次阅读

    Windows编程之变量和可变性综述

    Windows编程之变量和可变性综述
    发表于 08-24 14:22 3次下载

    Windows编程之变量与可变性详解

    Windows编程之变量与可变性详解
    发表于 08-24 14:52 8次下载

    单片机学习笔记————指针的第三大好处,指针作为数组在函数中的输出接口

    单片机学习笔记————指针的第三大好处,指针作为数组在函数中的输出接口
    发表于 11-26 10:51 1次下载
    单片机<b class='flag-5'>学习</b><b class='flag-5'>笔记</b>————<b class='flag-5'>指针</b>的第三大好处,<b class='flag-5'>指针</b>作为数组在函数中的输出接口

    单片机学习笔记————指针的第四大好处,指针作为数组在函数中的输入输出接口

    单片机学习笔记————指针的第四大好处,指针作为数组在函数中的输入输出接口
    发表于 11-26 10:51 14次下载
    单片机<b class='flag-5'>学习</b><b class='flag-5'>笔记</b>————<b class='flag-5'>指针</b>的第四大好处,<b class='flag-5'>指针</b>作为数组在函数中的输入输出接口

    单片机学习笔记————指针的第二大好处,指针作为数组在函数中的输入接口

    单片机学习笔记————指针的第二大好处,指针作为数组在函数中的输入接口
    发表于 11-26 10:51 7次下载
    单片机<b class='flag-5'>学习</b><b class='flag-5'>笔记</b>————<b class='flag-5'>指针</b>的第二大好处,<b class='flag-5'>指针</b>作为数组在函数中的输入接口

    CRust学习笔记:生命周期-1

    本系列文章是Jon Gjengset发布的CRust of Rust系列视频的学习笔记CRust of Rust是一系列持续更新的Rust中级教程。
    的头像 发表于 12-19 09:33 561次阅读

    CRust学习笔记:生命周期-2

    本系列文章是Jon Gjengset发布的CRust of Rust系列视频的学习笔记CRust of Rust是一系列持续更新的Rust中级教程。
    的头像 发表于 12-19 09:34 555次阅读

    CRust学习笔记:声明宏

    本系列文章是Jon Gjengset发布的CRust of Rust系列视频的学习笔记CRust of Rust是一系列持续更新的Rust中级教程。
    的头像 发表于 01-06 14:37 525次阅读

    python字符串序列操作和不可变性

    初识python字符串序列操作和不可变性。python字符串序列操作为序列通用操作,python字符串不可变性是对字符串的操作不会改变原始字符串
    的头像 发表于 02-23 15:01 636次阅读

    rust语言基础学习: 智能指针之Cow

    Rust中与借用数据相关的三个trait: Borrow, BorrowMut和ToOwned。理解了这三个trait之后,再学习Rust中能够实现写时克隆的智能指针Cow。
    的头像 发表于 05-22 16:13 2025次阅读

    面对不断增加的设计可变性,提高稳健性并最大限度地减少过度悲观情绪

    长期以来,半导体一直根据最坏情况的工艺、电压和温度(PVT)进行指定。在设计阶段,设计人员必须平衡性能与功耗和面积(PPA),努力实现所需的每瓦性能目标。为了评估可变性对电路性能的影响,设计人员采用
    的头像 发表于 05-24 16:37 316次阅读
    面对不断增加的设计<b class='flag-5'>可变性</b>,提高稳健性并最大限度地减少过度悲观情绪

    C++智能指针的底层实现原理

    C++智能指针的头文件: #include 1. shared_ptr: 智能指针从本质上来说是一个模板类,用类实现对指针对象的管理。 template class shared_ptr
    的头像 发表于 11-09 14:32 295次阅读
    C++<b class='flag-5'>智能指针</b>的底层实现原理