我的编程空间,编程开发者的网络收藏夹
学习永远不晚

揭开智能指针 Box 的神秘面纱

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

揭开智能指针 Box 的神秘面纱

本文转载自微信公众号「董泽润的技术笔记」,作者董泽润。转载本文请联系董泽润的技术笔记公众号。

熟悉 c++ 的肯定知道 shared_ptr, unique_ptr, 而 Rust 也有智能指针 Box, Rc, Arc, RefCell 等等,本文分享 Box 底层实现

Box 会在堆上分配空间,存储 T 值,并返回对应的指针。同时 Box 也实现了 trait Deref 解引用和 Drop 析构,当 Box 离开作用域时自动释放空间

入门例子

例子来自 the rust book, 为了演示方便,去掉打印语句

  1. fn main() { 
  2.     let _ = Box::new(0x11223344); 

将变量 0x11223344 分配在堆上,所谓的装箱,java 同学肯定很熟悉。让我们挂载 docker, 使用 rust-gdb 查看汇编实现

  1. Dump of assembler code for function hello_cargo::main: 
  2.    0x000055555555bdb0 <+0>: sub    $0x18,%rsp 
  3.    0x000055555555bdb4 <+4>: movl   $0x11223344,0x14(%rsp) 
  4. => 0x000055555555bdbc <+12>: mov    $0x4,%esi 
  5.    0x000055555555bdc1 <+17>: mov    %rsi,%rdi 
  6.    0x000055555555bdc4 <+20>: callq  0x55555555b5b0  
  7.    0x000055555555bdc9 <+25>: mov    %rax,%rcx 
  8.    0x000055555555bdcc <+28>: mov    %rcx,%rax 
  9.    0x000055555555bdcf <+31>: movl   $0x11223344,(%rcx) 
  10.    0x000055555555bdd5 <+37>: mov    %rax,0x8(%rsp) 
  11.    0x000055555555bdda <+42>: lea    0x8(%rsp),%rdi 
  12.    0x000055555555bddf <+47>: callq  0x55555555bd20 >> 
  13.    0x000055555555bde4 <+52>: add    $0x18,%rsp 
  14.    0x000055555555bde8 <+56>: retq 
  15. End of assembler dump. 

关键点就两条,alloc::alloc::exchange_malloc 在堆上分配内存空间,然后将 0x11223344 存储到这个 malloc 的地址上

函数结束时,将地址传递给 core::ptr::drop_in_place 去释放,因为编译器知道类型是 alloc::boxed::Box, 会掉用 Box 相应的 drop 函数

单纯的看这个例子,Box 并不神秘,对应汇编实现,和普通指针没区别,一切约束都是编译期行为

所有权

  1. fn main() { 
  2.     let x = Box::new(String::from("Rust")); 
  3.     let y = *x; 
  4.     println!("x is {}", x); 

这个例子中将字符串装箱,其实没必要这么写,因为 String 广义来讲本身就是一种智能指针。这个例子会报错

  1. 3 |     let y = *x; 
  2.   |             -- value moved here 
  3. 4 |     println!("x is {}", x); 
  4.   |                         ^ value borrowed here after move 

*x 解引用后对应 String, 赋值给 y 时执行 move 语义,所有权不在了,所以后续 println 不能打印 x

  1. let y = &*x; 

可以取字符串的不可变引用来 fix

底层实现

  1. pub struct Box< 
  2.     T: ?Sized, 
  3.     #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global
  4. >(Unique, A); 

上面是 Box 的定义,可以看到是一个元组结构体,有两个泛型参数:T 代表任意类型,A 代表内存分配器。标准库里 A 是 Gloal 默认值。其中 T 有一个泛型约束 ?Sized, 表示在编译时可能知道类型大小,也可能不知道,当然一般都用于不知道大小的场景,很少像上文一样存储 int

  1. #[stable(feature = "rust1", since = "1.0.0")] 
  2. unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Box { 
  3.     fn drop(&mut self) { 
  4.         // FIXME: Do nothing, drop is currently performed by compiler. 
  5.     } 

这是 Drop 实现,源码里也说了,由编译器实现

  1. #[stable(feature = "rust1", since = "1.0.0")] 
  2. impl Deref for Box { 
  3.     type Target = T; 
  4.  
  5.     fn deref(&self) -> &T { 
  6.         &**self 
  7.     } 
  8.  
  9. #[stable(feature = "rust1", since = "1.0.0")] 
  10. impl DerefMut for Box { 
  11.     fn deref_mut(&mut self) -> &mut T { 
  12.         &mut **self 
  13.     } 

实现了 Deref 可以定义解引用行为,DerefMut 可变解引用。所以 *x 对应着操作 *(x.deref())

适用场景

官网提到以下三个场景,本质上 Box 和普通指针区别不大,所以用处不如 Rc, Arc, RefCell 广

  • 当类型在编译期不知道大小,但代码场景还要求确认类型大小的时候
  • 当你有大量数据,需要移动所有权,而不想 copy 数据的时候
  • trait 对象,或者称为 dyn 动态分发常用在一个集合中存储不同的类型上,或者参数指定不同的类型

官网有一个链表的实现

  1. enum List { 
  2.     Cons(i32, List), 
  3.     Nil, 

上面代码是无法运行的,道理也很简单,这是一种递归定义。对应 c 代码也是不行的,我们一般要给 next 类型定义成指针才行

  1. enum List { 
  2.     Cons(i32, Box), 
  3.     Nil, 
  4.  
  5. use crate::List::{Cons, Nil}; 
  6.  
  7. fn main() { 
  8.     let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))))); 

 

官网给的解决方案,就是将 next 变成了指针 Box , 算是常识吧,没什么好说的

 

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

揭开智能指针 Box 的神秘面纱

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

揭开智能指针 Box 的神秘面纱

Box 会在堆上分配空间,存储 T 值,并返回对应的指针。同时 Box 也实现了 trait Deref 解引用和 Drop 析构,当 Box 离开作用域时自动释放空间。
智能指针Box2024-12-02

揭开 Python CPython 的神秘面纱

Python CPython 是 Python 语言最受欢迎的实现,在本文中,我们将深入探索 CPython 的内部机制,包括其架构、内存管理和优化技术,帮助您更深入地理解 Python 的底层实现。
揭开 Python CPython 的神秘面纱
2024-03-04

揭开 Java JUnit 的神秘面纱:终极指南

JUnit 是一种用于 Java 单元测试的广泛使用的框架。它提供了丰富的功能,使开发人员能够轻松编写、运行和维护可信赖的代码。本文将深入探讨 JUnit 框架,全面介绍其特性、使用和最佳实践,帮助开发人员充分利用它编写健壮可靠的代码。
揭开 Java JUnit 的神秘面纱:终极指南
2024-02-16

揭开云计算的神秘面纱

下课铃声响了,大雄、静香和叮当一起背着书包放学啦。走着走着大雄突然“啊”了一声说,我U盘忘带了,一会还要去打印课件呢,我得回去取。

揭开SQL中NULL的神秘面纱

这篇文章将揭开 SQL 中 NULL 的神秘面纱。这个问题可能困扰着很多初级开发者。在查询数据库时,如果你想知道一个列是否为 NULL,SQL 查询语句该怎么写呢?是不是这样:SELECT * FROM SOME_TABLEWHER
2023-01-04

揭开 Nuxt.js 部署的神秘面纱:逐步指南

Nuxt.js 部署是一项至关重要的任务,需要仔细规划和执行。本指南将引导您逐步完成整个过程,确保您的应用程序顺利上线。
揭开 Nuxt.js 部署的神秘面纱:逐步指南
2024-03-06

揭开 PHP PDO 神秘面纱:初学者指南

PHP PDO(PHP 数据对象)是一个用于连接和操作数据库的强大工具。它提供了一个统一、面向对象的接口来访问各种数据库,并且比原生 PHP 数据库扩展提供了更高级的功能和安全性。本文将指导初学者使用 PDO 连接、查询和操作数据库。
揭开 PHP PDO 神秘面纱:初学者指南
2024-02-17

揭开JVM中TLAB中的神秘面纱

在开始文章之前,我这里暂且认为大家已经明白了JVM创建对象分配内存地址的流程,也知道JVM内存划分。基于人道主义我还是放一张图吧,大家对照着看。
JVM对象内存2024-12-03

揭开 Python logging 模块的神秘面纱:全方位指南

Python logging 模块提供了强大的日志记录功能,可帮助开发者监控和调试应用程序。本文将深入介绍 logging 模块,涵盖其基本概念、高级用法以及最佳实践。
揭开 Python logging 模块的神秘面纱:全方位指南
2024-03-06

揭开 JavaScript 数据类型的神秘面纱

JavaScript 数据类型的多样性让程序员掌握其细微差别至关重要。本文深入探究 JavaScript 的数据类型,阐明它们的特点和应用,以帮助您写出高效、健壮的代码。
揭开 JavaScript 数据类型的神秘面纱
2024-03-06

揭开勒索软件LostTrust的神秘面纱

LostTrust勒索软件于2023年3月开始积极活动,并攻击了多个企业和组织,但直到9月份才被广大研究人员所知晓。

揭开 Java Iterator 与 Iterable 的神秘面纱:初学者指南

Java Iterator 与 Iterable 是 Java 集合框架中必不可少的组件。它们提供了一种统一的方式来遍历集合中的元素。在本文中,我们将深入探讨 Iterator 和 Iterable,并通过示例代码演示如何使用它们。
揭开 Java Iterator 与 Iterable 的神秘面纱:初学者指南
2024-02-13

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录