diff --git "a/02-\345\206\205\345\255\230.md" "b/02-\345\206\205\345\255\230.md" index 0294736..2b3fff1 100644 --- "a/02-\345\206\205\345\255\230.md" +++ "b/02-\345\206\205\345\255\230.md" @@ -1,8 +1,8 @@ # RUST标准库内存模块代码分析 内存模块的代码路径举例如下(以作者电脑上的路径): -%USER%\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\alloc\*.* -%USER%\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ptr\*.* -%USER%\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\mem\*.* +%USER%\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\alloc\*.* +%USER%\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ptr\*.* +%USER%\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\mem\*.* %USER%\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\intrinsic.rs %USER%\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\alloc\src\alloc.rs @@ -78,7 +78,7 @@ pub trait Thin = Pointee; * 对于切片类型,例如`[T]`类型,元数据是数组元素的数目值,元数据类型是usize * 对于trait对象,例如 dyn SomeTrait, 元数据是 [DynMetadata][DynMetadata](后面代码解释) (例如:DynMetadata) -随着RUST的发展,有可能会根据需要引入新的元数据种类。 + 随着RUST的发展,有可能会根据需要引入新的元数据种类。 在标准库代码当中没有指针类型如何实现Pointee Trait的代码,编译器针对每个类型自动的实现了Pointee。 如下为rust编译器代码的一个摘录 @@ -207,7 +207,7 @@ intrinsics模块中的函数由编译器内置实现,并提供给其他模块 内存比较函数: `intrinsics::raw_eq(a: &T, b: &T) -> bool` 内存比较,类似C语言memcmp -`pub fn ptr_guaranteed_eq(ptr: *const T, other: *const T) -> bool` 判断两个指针是否判断, 相等返回ture, 不等返回false +`pub fn ptr_guaranteed_eq(ptr: *const T, other: *const T) -> bool` 判断两个指针是否相等, 相等返回ture, 不等返回false `pub fn ptr_guaranteed_ne(ptr: *const T, other: *const T) -> bool` 判断两个指针是否不等,不等返回true @@ -263,8 +263,8 @@ RUST也提供了一些其他的裸指针创建关联函数: RUST裸指针类型转换时,经常使用以上两个函数获得需要的指针类型。 -切片类型的裸指针创建函数如下: -`ptr::slice_from_raw_parts(data: *const T, len: usize) -> *const [T] ` +切片类型的裸指针创建函数如下: +`ptr::slice_from_raw_parts(data: *const T, len: usize) -> *const [T] ` `ptr::slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T]` 由裸指针类型及切片长度获得切片类型裸指针,调用代码应保证data事实上就是切片的裸指针地址。 由类型裸指针转换为切片类型裸指针最突出的应用之一是内存申请,申请的内存返回 * const u8的指针,这个裸指针是没有包含内存大小的,只有头地址,因此需要将这个指针转换为 * const [u8],将申请的内存大小包含入裸指针结构体中。 @@ -301,7 +301,7 @@ ptr模块的函数大部分逻辑都比较简单。很多就是对intrinsic 函 调用以上的函数要注意,如果后继要把返回的指针转换成引用,那必须保证T类型与U类型内存布局完全一致。如果仅仅是将返回值做数值应用,则此约束可以不遵守,cast函数转换后的类型通常由编译器自行推断,有时需要仔细分析。 裸指针与引用之间的类型转换: -```*const T::as_ref<‘a>(self) -> Option<&’a T>``` 将裸指针转换为引用,因为*const T可能为零,所有需要转换为``Option<& ‘a T>``类型,转换的安全性由程序员保证,尤其注意满足RUST对引用的安全要求。这里注意,**生命周期标注表明转换后的生命周期实际上与原变量的生命周期相独立**。因此,生命周期的正确性将由调用代码保证。如果没有标注,则返回的引用的生命周期应该小于self,遵循函数参数及返回值的生命周期规则。 +```*const T::as_ref<‘a>(self) -> Option<&’a T>``` 将裸指针转换为引用,因为*const T可能为零,所有需要转换为``Option<& ‘a T>``类型,转换的安全性由程序员保证,尤其注意满足RUST对引用的安全要求。这里注意,**生命周期标注表明转换后的生命周期实际上与原变量的生命周期相独立**。因此,生命周期的正确性将由调用代码保证。如果没有标注,则返回的引用的生命周期应该小于self,遵循函数参数及返回值的生命周期规则。 ```*mut T::as_ref<`a>(self)->Option<&`a T>``` 同上 ```*mut T::as_mut<`a>(self)->Option<&`a mut T>```同上,但转化类型为 &mut T。 @@ -427,7 +427,7 @@ pub struct ManuallyDrop { //所有权转移到结构体内部,value生命周期结束时不会引发drop ManuallyDrop { value } } -``` +``` `ManuallyDrop::into_inner(slot: ManuallyDrop)->T`, 将封装的T类型变量所有权转移出来,转移出来的变量生命周期终止时,编译器会自动调用类型的drop。 ```rust pub const fn into_inner(slot: ManuallyDrop) -> T { @@ -657,7 +657,7 @@ assume_init_read是不消费self的情况下获得内部T变量,内部T变量 //此处必须用ptr::read_unaligned,因为不确定字节是否对齐 unsafe { ptr.read_unaligned() } } -``` + ``` 例子中,为了从byte串中读取一个usize,需要用read_unaligned来获取值,不能象C语言那样通过指针类型转换直接获取值。 `ptr::write(dst: *mut T, src: T)` 代码如下: @@ -830,7 +830,7 @@ impl const From<&T> for NonNull { //self.cast.as_ptr将NonNull>转换为 *mut MaybeUninit unsafe { &*self.cast().as_ptr() } } -``` +``` ```NonNull::as_uninit_mut<'a>(&self) -> &'a mut MaybeUninit``` ```NonNull<[T]>::as_uninit_slice<'a>(&self) -> &'a [MaybeUninit]``` ```rust @@ -855,9 +855,9 @@ impl const From<&T> for NonNull { 对于RUST从堆内存申请的内存块,其指针都是用`Unique`封装后来作为智能指针结构体内部成员变量,保证智能指针结构体拥有申请出来的内存块的所有权。 `Unique`模块的函数及代码与`NonNull`函数代码相类似,此处不分析。 -`Unique::cast(self)->Unique` 类型转换,程序员应该保证T和U的内存布局相同 -`Unique::::new(* mut T)->Option` 此函数内部判断* mut T是否为0值 -`Unique::::new_unchecked(* mut T)->Self` 封装* mut T, 调用代码应该保证* mut T的安全性 +`Unique::cast(self)->Unique` 类型转换,程序员应该保证T和U的内存布局相同 +`Unique::::new(* mut T)->Option` 此函数内部判断* mut T是否为0值 +`Unique::::new_unchecked(* mut T)->Self` 封装* mut T, 调用代码应该保证* mut T的安全性 `Unique::as_ptr(self)->* mut T` `Unique::as_ref(&self)->& T` 因为Unique具备所有权,此处&T的生命周期与self相同,不必特别声明声明周期 `Unique::as_mut(&mut self)->& mut T` 同上 @@ -991,8 +991,9 @@ pub struct Layout { *`NonZeroUsize`是一种非0值的usize, 这种类型主要应用于不可取0的值,本结构中, 字节对齐属性变量不能被置0,所以用`NonZeroUsize`来确保安全性。如果用usize类型,那代码中就可能会把0置给align_,导致bug产生。这是RUST的一个设计规则,所有的约束要在类型定义即显性化,从而使bug在编译中就被发现。* 每一个RUST的类型都有自身独特的内存布局Layout。一种类型的Layout可以用`intrinsic::::size_of()`及`intrinsic::::min_align_of()`获得的类型内存大小和对齐来获得。 -RUST的内存布局更详细原理阐述请参考[RUST内存布局] (https://doc.rust-lang.org/nomicon/data.html), +RUST的内存布局更详细原理阐述请参考[RUST内存布局](https://doc.rust-lang.org/nomicon/data.html) Layout比较有典型意义的函数: + ```rust impl Layout { ... @@ -1229,11 +1230,11 @@ Allocator使用GlobalAlloc接口获取内存,然后将GlobalAlloc申请到的* RUST支持const 及 static类型的全局变量,且static支持可写操作。所有对static的写操作都是unsafe的。 需要特别注意的,全局变量不支持非Copy trait类型所有权转移,这也很好理解,所有权转移实际上一个内存"move“的操 作。但static变量的内存显然是没有办法"move"的。 - + static这一性质导致如果要有转移所有权的操作,必须使用mem::replace的方式进行,RUST标准库中很多类型基于mem::replace实现类型自身的replace方法或take方法。 C/C++程序员比较习惯于设计全局变量及其变种静态方法,RUST的全局变量所有权的限制会对这个设计思维有较大的冲击 -推荐这篇全局变量的链接: [rust-global-variable](https://www.sitepoint.com/rust-global-variables/) +推荐这篇全局变量的链接: [rust-global-variable](https://www.sitepoint.com/rust-global-variables/) 下面是摘录的一张图 ![全局变量使用](./rust%E5%85%A8%E5%B1%80%E5%8F%98%E9%87%8F.png) # RUST所有权,生命周期,借用探讨