本文是对 Jon Gjengset 写的新书 《Rust for Rustaceans》样章第二章的中文试译初稿。出于对 Jon 的尊敬,以及想了解 Jon 眼中的 Rust ,我打算翻译一下这本书。发出来让大家看看翻译效果,欢迎指正。
在Rust入坑指南:核心概念一文中我们介绍了Rust的几个核心概念:所有权(Ownership)、所有权转移和所有权借用。今天就来介绍Rust中的另外一个核心概念:生命周期。
在了解了Rust中的所有权、所有权借用、生命周期这些概念后,相信各位坑友对Rust已经有了比较深刻的认识了,今天又是一个连环坑,我们一起来把智能指针刨出来,一探究竟。
rust 通过使用引用计数智能指针 Rc 和 Arc 来解决上面的问题。当我们对一个被 Rc 所标识的数据进行 clone() 的时候,并不会复制其内部数据,只是增加引用计数,而当一个 Rc 被 drop 的时候,只会减少其引用计数,直到引用计数为0,此时才会真正清除对应的内存。
这是来自 Google OpenTitan 团队,给嵌入式 C 程序员专门打造的一份 Rust 指南。
Scoped Thread 对应的是一种叫做结构化并发(Structured Concurrency)概念的实现。
对于许多编程语言默认提供的锁,加锁、放锁需要手动进行。手动加锁可以理解(这不废话嘛),但是,手动放锁的时机,总是难以控制。比如:在临界区内,执行过程中,如果程序出错了,在异常处理的过程中,忘记放锁,那么就会造成其他进程无法获得这个锁。传统的做法就是,人工寻找所有可能的异常处理路径,添加放锁的代码。这样做的话,能解决问题,但非常的繁琐,尤其是有多个锁的时候,更加如此。
不夸张地讲,Box<T>就是【堆·数据】在【栈】内存中的“全权·代理人”。具有同类特点的【智能指针】还包括String和CString等。
我曾经有过的所有这些对生命周期的误解,现在有很多初学者也深陷于此。我用到的术语可能不是标准的,所以下面列了一个表格来解释它们的用意。
即:在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用。引用必须总是有效的。
题图来自 Rust 101 — Everything you need to know about Rust[1]
Rust 可以在 堆 上存储数据。Rust 语言中的某些类型,如 向量 Vector 和 字符串对象 String 默认就是把数据存储在 堆 上的。
如果你一直在订阅这个系列,关于所有权的那篇文章[1]可能给你带来了这种印象——Rust 确实是个好东西,C++不应该在生产环境中使用。智能指针可能会改变你的想法。用现代的话来说,Smart pointers 是指那些有点(嗯......)额外(东西)的指针。他们本质上还是管理其所指向的对象的内存地址,并且当对象不再被使用的时候会将其释放。这消除了很多因不恰当的内存管理而引起的 bug,并使得编程不再那么枯燥乏味。C++智能指针为原始指针提供了一个安全的替代方案,而 Rust 智能指针则在保证安全的前提下扩展了语言功能。
N-API是Node API的简写,同时也是nodejs的JS VM(链)接入原生模块.node文件的应用程序二进制接口(i.e. ABI)。借助N-API引入的抽象隔离,升级nodejs运行时(虚拟机)
在Rust源代码中,rust/library/core/src/future/ready.rs文件的作用是定义了一个名为Ready的Future类型。Ready是一个简单的Future实现,它立即返回一个给定的值。
题图来自 Why you should use Python and Rust together[1]
本文内容译自Lock-freedom without garbage collection,中间有少量自己的修改.
在Rust源代码中,rust/library/alloc/src/vec/mod.rs这个文件是Rust标准库中的Vec类型的实现文件。Vec是一个动态大小的数组类型,在内存中以连续的方式存储其元素。
两个指针a和b有可能互为别名(俩指针指向同一数据),所以clang在编译该代码的时候,会在寄存器中存储两次(%rsi),%eax来防止这种情况。当然也可以使用restrict关键字来对参数进行约束,显式地告诉编译器这俩指针不可能互为别名(但在C语言中,restrict关键字仅仅是告诉编译器可以优化,它不会帮助开发者进行检查,也就是说,很可能因为开发者没有遵循此契约而引发未定义行为)。从而生成的优化代码中只存储一次寄存器。
去年秋天,我正在开发一个库,创建一套安全的API,实现在一个 io-uring 实例的基础上执行 future。虽然最后发布了一个叫 iou 的 liburing 的 binding 库,但其与 future 集成的 ostkreuz 库最终未能发布。我不知道将来是否会继续这项工作,但是有些人已经开始开发目标类似的库了,因此我想就我在 io-uring 和 Rust 的 future 模型上的学习情况做一些笔记。这篇文章假定你对 io-uring API 有一定了解。这个文档(https://kernel.dk/io_uring.pdf)提供了关于 io-uring 的高级概述。
智能指针虽然也号称指针,但是它是一个复杂的家伙:通过比引用更复杂的数据结构,包含比引用更多的信息,例如元数据,当前长度,最大可用长度等。引用和智能指针的另一个不同在于前者仅仅是借用了数据,而后者往往可以拥有它们指向的数据,然后再为其它人提供服务。智能指针往往是基于结构体实现,它与我们自定义的结构体最大的区别在于它实现了 Deref 和 Drop 特征:
题图来自 Programming languages: How Google is using Rust to reduce memory safety vulnerabilities in Android[1]
Box 允许将一个值放在堆上而不是栈上,留在栈上的则是指向堆数据的指针。Box 是一个指向堆的智能指针,当一个 Box 超出作用域时,它的析构函数被调用,内部对象被销毁,堆上的内存被释放。
在Rust源代码中,rust/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs文件的作用是进行异常处理和记录借用关系。
本文转载自知乎地址:https://zhuanlan.zhihu.com/p/92679351?utm_source=wechat_session&utm_medium=social&utm_oi=5
在Rust编译器的源代码中,rust/compiler/rustc_const_eval/src/util/check_validity_requirement.rs文件的作用是进行验证要求的检查。具体而言,该文件定义了函数check_validity_requirement,它用于检查常量表达式的有效性要求。
熟悉 c++ 的肯定知道 shared_ptr, unique_ptr, 而 Rust 也有智能指针 Box, Rc, Arc, RefCell 等等,本文分享 Box 底层实现
在编程语言中,对堆对象的内存管理是一个麻烦又复杂的问题。一不小心就会带来问题,比如JS里一直引用一个已经不使用的对象导致gc无法回收,或者C++里多个变量指向同一块内存导致重复释放。本文简单探讨一下关于对象所有权的问题。
在 Rust 中,早期释放(2-Phase Drop)是一种特殊的机制,用于在析构函数中提前释放资源。通过使用早期释放机制,我们可以控制资源的释放顺序,避免资源泄漏和不一致的状态。
全球有成千上万的工作是为那些会使用C++编程的人准备的,但是对于Rust的程序员来说明显需求很少,但是在Stack Overflow的2020年开发人员调查中,Rust成为了最受欢迎的编程语言。
此时可以使用Box<T>指针指向嵌套的列表,得到cons list类型的结构体。(指针的内存大小是已知的,但列表的大小是在进行结构体声明时未知的)
文件mod.rs位于Rust编译器源代码中的rustc_data_structures/src/graph/dominators目录下。这个文件的作用是实现支配树(dominator tree)的计算算法。
该站点专注于记录世界各地Rust各大活动的时间线,开源项目,大家可以提交活动信息。
RAII全称是Resource Acquisition Is Initialization,翻译过来是资源获取即初始化,RAII机制用于管理资源的申请和释放。对于资源,我们通常经历三个过程,申请,使用,释放,这里的资源不仅仅是内存,也可以是文件、socket、锁等等。但是我们往往只关注资源的申请和使用,而忘了释放,这不仅会导致内存泄漏,可能还会导致业务逻辑的错误,RAII就用来解决此类问题。
科学无非就是在自然界的多样性中寻求统一性(或者更确切地说,是在我们经验的多样性中寻求统一性)。用 Coleridge 的话说,诗歌、绘画、艺术,同样是在多样性中寻求统一性。
文件rust/compiler/rustc_codegen_ssa/src/traits/declare.rs的作用是定义了一个Declare trait,用于声明函数、变量和全局变量等需要使用的实体。
在Rust源代码中,rust/library/alloc/benches/slice.rs文件的作用是对&[T]类型(切片类型)进行性能基准测试。该文件包含了对切片类型的一系列操作的基准测试,例如切片迭代、切片排序、切片的iter和into_iter方法等。
但上述情景似乎难以兼顾:只要指向值的指针仍然存在,释放这个值就必然会让这些指针悬空。几乎所有主流编程语言都只能在两个阵营中“二选一”,这取决于它们从中放弃了哪一项。
这些模块定义了Rust标准库中主要的 trait、类型定义和功能模块,为开发者提供了常用的系统级功能,比如内存管理、IO操作、线程同步等的 abstraction。
科学无非就是在自然界的多样性中寻求统一性(或者更确切地说,是在我们经验的多样性中寻求统一性)。用 Coleridge 的话说,诗歌、绘画、艺术,同样是在多样性中寻求统一性
本文是《Rust in action》学习总结系列的第四部分,更多内容请看已发布文章:
其实我一直弄不明白一点,那就是计算机技术的发展,是让这个世界变得简单了,还是变得更复杂了。 当然这只是一个玩笑,可别把这个问题当真。
Wikipedia 将 FFI 定义为一种机制,通过这种机制,用一种编程语言编写的程序可以调用或使用用另一种编程语言编写的服务。
首先回答第2个问题,分配在栈上还是堆上是由编译器决定的,编译器会做逃逸分析(escape analysis),当发现变量的作用域没有超出函数范围,就可以在栈上,反之则必须分配在堆上。
最近在极客时间上看到吴老师的《现代C++实战30讲》,觉得很是不错,于是学习一下,本文中的一些文字概念引用自这里。同时,对于这个课的代码我放在了我的《C++那些事》仓库里面,点击阅读原文,或者下面链接,即可获取。欢迎star!
很抱歉,第2篇距离第1篇长达3个月。。。工作繁忙加上家里事多。。。不找客观原因了,咱们开始聊聊Rust的所有权系统。
如果您喜欢本文,请多多点赞转发,以及请我喝杯咖啡,这样可以帮助我加强写作动力。感谢您!
题图来自 HOW TO LEARN RUST PROGRAMMING LANGUAGE IN 10 MINUTES[1]
在Rust源代码中,rust/compiler/rustc_trait_selection/src/traits/select/mod.rs文件的作用是实现Rust编译器的trait选择器。
领取专属 10元无门槛券
手把手带您无忧上云