Rust 提供两种裸指针类型:
*mut T
:可变裸指针,可以读写指向的内容;*const T
:不可变裸指针,只能读而不能修改指向的内容;
裸指针的主要使用场景是 FFI,如 C 函数的指针类型需要裸指针来声明和传递参数等。
// libc 的 malloc 和 free 使用裸指针来管理内存
#[allow(unused_extern_crates)]
extern crate libc;
use std::mem;
unsafe {
let my_num: *mut i32 = libc::malloc(mem::size_of::<i32>()) as *mut i32;
if my_num.is_null() {
panic!("failed to allocate memory");
}
libc::free(my_num as *mut libc::c_void);
}
通过对裸指针来保存对象,如 *ptr = data
,会先 ptr 指向的 old value。但通过裸指针、MaybeUninit<T>
的 write()
方法或 std::ptr::write()
来写入新对象时,并不会 drop old value。
// 使用 MaybeUninit 来初始化 struct field
#[derive(Debug, PartialEq)]
pub struct Foo {
name: String,
list: Vec<u8>,
}
let foo = {
let mut uninit: MaybeUninit<Foo> = MaybeUninit::uninit();
// 转换为裸指针
let ptr = uninit.as_mut_ptr();
// Initializing the `name` field. Using `write` instead of assignment via `=` to
// not call `drop` on the old, uninitialized value.
unsafe { std::ptr::addr_of_mut!((*ptr).name).write("Bob".to_string()); }
// Initializing the `list` field. If there is a panic here, then the `String` in
// the `name` field leaks.
unsafe { std::ptr::addr_of_mut!((*ptr).list).write(vec![0, 1, 2]); }
unsafe { uninit.assume_init() }
};
裸指针可以是 unaligned 或 null。当解引用裸指针时,它必须是 non-null 和 aligned,这里的 aligned 是指指针的地址如 *const T
是 std::mem::align_of::<T>()
的倍数。
unsafe #
裸指针的 unsafe 体现在:
- 允许忽略借用规则:可以同时拥有同一个内存地址的可变和不可变指针,或者拥有指向同一个地址的多个可变指针;
- 不能保证总是指向有效的内存地址;
- 允许为空(null);
fn main() {
let p;
{
let x = 5;
p = &x as *const i32; // 合法
}
unsafe {
// 未定义行为:p 悬垂了,但编译器不报错
println!("{}", *p); // 6
// 未定义行为:向只读对象写内容
let pm = p as *mut i32;
// 编译器不检查,不报错
*pm = 6;
println!("{}", *pm); // 6
}
}
创建裸指针 #
从借用创建裸指针:
- 可以使用安全代码创建裸指针,但只能在
unsafe block
中使用(解引用)裸指针; *const T 和 *mut T
之间可以相互转换;- 裸指针不拥有值的所有权;
let mut num = 5;
// 有效的裸指针:可以同时创建的不可变和可变裸指针
let r1 = &num as *const i32;
let r2 = &mut num as *mut i32;
// 可以将 *const T 转换为 *mut T
let r2 = r1 as *mut i32;
let r2 = r1.cast_mut();
// 可能无效的裸指针
let address = 0x012345usize;
let r = address as *const i32;
let my_num: i32 = 10;
// 引用类型自动 type coercing 到 raw pointer
let my_num_ptr: *const i32 = &my_num;
let mut my_speed: i32 = 88;
let my_speed_ptr: *mut i32 = &mut my_speed;
let mut x = 10;
// as 运算符将借用转换为 raw pointer
let ptr_x = &mut x as *mut i32;
fn very_trustworthy(shared: &i32) {
unsafe {
let mutable = shared as *const i32 as *mut i32;
*mutable = 20;
}
}
// 只能在 unsafe block 中解引用裸指针
unsafe {
println!("r1 is: {}", *r1);
println!("r2 is: {}", *r2);
}
如果要获得 boxed 值的裸指针,需要先解引用 Box 再借用,这并不会转移 boxed 值的所有权。
- Box
::into_raw() 函数消耗 Box 的同时返回一个裸指针:
let my_num: Box<i32> = Box::new(10);
// &*my_num 的结果是 &i32,可以转换为 *const i32
let my_num_ptr: *const i32 = &* my_num;
let mut my_speed: Box<i32> = Box::new(88);
let my_speed_ptr: *mut i32 = &mut *my_speed;
let my_speed: Box<i32> = Box::new(88);
// 转移了 my_speed 的所有权。
let my_speed: *mut i32 = Box::into_raw(my_speed);
unsafe {
drop(Box::from_raw(my_speed));
}
let y = Box::new(20);
let ptr_y = &*y as *const i32;
unsafe {
*ptr_x += *ptr_y;
}
assert_eq!(x, 30);
使用宏 std::ptr::addr_of!()
和 std::ptr::addr_of_mut!()
创建表达式值的裸指针。
其它创建裸指针的方式:
- 很多类型提供了
as_ptr/as_mut_ptr()
方法来返回裸指针; - Owning 类型, 如
Box/Rc/Arc 的 into_raw/from_raw()
方法来生成裸指针或从裸指针创建对象; - 使用 as 表达式;
&MaybeUninit<T>、&mut MaybeUninit<T>、&T、 &mut T、*const T、*mut T
的内存布局、大小、对齐都一致,可以使用 as 表达式相互转换:
// as 表达式支持的转换类型:https://doc.rust-lang.org/reference/expressions/operator-expr.html#r-expr.as.pointer
// 将裸指针转换为 usize
let ptr_num_cast = ptr as *const i32 as usize;
// 将裸指针转换为借用
// 这是因为 &T, &mut T, *const T 和 *mut T 内存布局是一致的,所以可以转换。
let ptr: *mut i32 = &mut 0;
let ref_casted = unsafe { &mut *ptr };
// 将 &mut T 转换为 &mut U:
let ptr = &mut 0;
let val_casts = unsafe { &mut *(ptr as *mut i32 as *mut u32) };
// 将 &[MaybeUninit<u8>] 转换为 *const MaybeUninit<u8>, 然后使用 as 转换为裸指针
let ma = [MaybeUninit<u8>; 32];
let mas: &[MaybeUninit<u8>] = ma;
let mac = unsafe {mas.as_ptr() as *const u8 as *mut u8}
packed struct 和未对齐的裸指针 #
默认情况下,struct 对象的 field 会通过 pading 来对齐。通过添加 packed attr,可以关闭 struct field padding 对齐机 制,这样 struct 的某个 field 可能是未对齐的。
对于未对齐的 field 不能进行借用(&T/&mut T),但是裸指针是可以未对齐的,有两种创建未对齐的裸指针方式:
- 通过
std::ptr::addr_of!()
和std::ptr::addr_of_mut!()
宏:
#[derive(Debug, Default, Copy, Clone)]
#[repr(C, packed)]
struct S {
aligned: u8,
unaligned: u32,
}
let s = S::default();
let p = std::ptr::addr_of!(s.unaligned);
- 或者通过
&raw const
或&raw mut
创建裸指针(不能使用 &T 或 &mut T,因为它要求表达式必须是对齐的):
#[derive(Debug, Default, Copy, Clone)]
#[repr(C, packed)]
struct S {
aligned: u8,
unaligned: u32,
}
let s = S::default();
let p = &raw const s.unaligned; // not allowed with coercion
null 类型裸指针 #
裸指针可以为 null:
- 创建 null 指针:
std::ptr::null::<T>()
创建*const T;
std::ptr::null_mut::<T>()
创建*mut T;
- 检查 null 指针:使用
is_null/as_ptr/as_mut_ptr()
方法;
fn option_to_raw<T>(opt: Option<&T>) -> *const T {
match opt {
None => std::ptr::null(),
Some(r) => r as *const T
}
}
assert!(!option_to_raw(Some(&("pea", "pod"))).is_null());
assert_eq!(option_to_raw::<i32>(None), std::ptr::null());
裸指针的一些限制 #
- 必须显式解引用,
(*raw).field 或 (*raw).method(...)
; - 裸指针不支持 Deref;
- 裸指针的比较运算,如 == 或 <, 比较的是指针地址, 而非指向的内容;
- 裸指针没有实现 Display, 但实现了 Debug 和 Pointer;
- 不支持裸指针的算术运算符, 但是可以使用库函数来进行运算;
- 裸指针没有实现 Send/Sync/Unpin, 不能跨线程或 async spawn 中使用;
let trucks = vec!["garbage truck", "dump truck", "moonstruck"];
let first: *const &str = &trucks[0];
let last: *const &str = &trucks[2];
assert_eq!(unsafe { last.offset_from(first) }, 2); // 返回偏移的元素数量
assert_eq!(unsafe { first.offset_from(last) }, -2);
// as 运算符支持将引用转换为 raw pointer(反过来不支持), 但是可能需要多次转换
&vec![42_u8] as *const String; // error: invalid conversion
&vec![42_u8] as *const Vec<u8> as *const String; // permitted
Rust 的 array/slice/vector 都是连续的内存地址块,每个元素占用固定大小的(std::mem::size_of<T>
)内存:
fn offset<T>(ptr: *const T, count: isize) -> *const T where T: Sized
{
let bytes_per_element = std::mem::size_of::<T>() as isize;
let byte_offset = count * bytes_per_element;
// 使用 ptr as isize 获得指针的实际值,然后进行数学运算,再将它转回 *const T
(ptr as isize).checked_add(byte_offset).unwrap() as *const T
}
*const T 和 *mut T 方法 #
*const T 和 *mut T
是 Rust 基本类型,标准库为其定义了一些方法(std::ptr module
也提供了一些函数来操作裸指针):
- 是否为 null :
pub fn is_null(self) -> bool
- 转换为 U 的指针 :
pub const fn cast<U>(self) -> *const U
- 返回指定 count 个对象的偏移指针 :
pub const unsafe fn offset(self, count: isize) -> *const T
- 计算相对于指定指针的对象数量 :
pub const unsafe fn offset_from(self, origin: *const T) -> isize
- 计算增加 count 个对象的偏移指针 :
pub const unsafe fn add(self, count: usize) -> *const T
- 计算减少 count 个对象的偏移指针 :
pub const unsafe fn sub(self, count: usize) -> *const T
- 读取指针内容,但是不 move self,返回 T 类型对象:
pub const unsafe fn read(self) -> T
- 拷贝
count * size_of<T>()
字节,src 和 dest 可以重叠 :pub const unsafe fn copy_to(self, dest: *mut T, count: usize)
- 写入 T 值,但是不 read & drop 原来的值 :
pub unsafe fn write(self, val: T)
- 替换 T 值,但是不 read & drop 原来的值 :
pub unsafe fn replace(self, src: T) -> T
- 交换两个地址的值 :
pub unsafe fn swap(self, with: *mut T)
从裸指针创建 T 对象:
- 使用
read()
方法; - 使用
Box::from_raw()
方法;
*const T
实现的方法:
impl<T> *const T where T: ?Sized
pub fn is_null(self) -> bool
let s: &str = "Follow the rabbit";
let ptr: *const u8 = s.as_ptr();
assert!(!ptr.is_null());
// 将 *const T 转换为 *const U
pub const fn cast<U>(self) -> *const U
// 将 *const T 转换为 *mut T
pub const fn cast_mut(self) -> *mut T
pub fn with_metadata_of<U>(self, meta: *const U) -> *const U where U: ?Sized
// 返回 *const T 指针的整型值,后续可以对它进行数学运算
pub fn addr(self) -> usize
pub fn expose_addr(self) -> usize
// 反过来,使用指定的 addr 创建一个裸指针
pub fn with_addr(self, addr: usize) -> *const T
// 使用指定的 f 将自身地址映射到新地址
pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> *const T
// 返回自身指针和 Metadata
pub fn to_raw_parts(self) -> (*const (), <T as Pointee>::Metadata)
// 将裸指针转换为借用,如果指针是 null 则返回 None
pub unsafe fn as_ref<'a>(self) -> Option<&'a T>
pub unsafe fn as_uninit_ref<'a>(self) -> Option<&'a MaybeUninit<T>>
let ptr: *const u8 = &10u8 as *const u8;
unsafe {
if let Some(val_back) = ptr.as_ref() {
println!("We got back the value: {val_back}!");
}
}
// 计算经过 count 个对象后的 offset 地址,offset 起始地址和加了 count*size 的结束地址都必须位于已分配对象的内存范围内,否则是 UB。
pub const unsafe fn offset(self, count: isize) -> *const T
let s: &str = "123";
let ptr: *const u8 = s.as_ptr();
unsafe {
println!("{}", *ptr.offset(1) as char);
println!("{}", *ptr.offset(2) as char);
}
// 和 offset() 类似,但是使用字节数偏移
pub const unsafe fn byte_offset(self, count: isize) -> *const T
// wrapping_offset 和 offset 相比,不要求起始和结束地址都位于已分配对象的内存范围内。
pub const fn wrapping_offset(self, count: isize) -> *const T
pub const fn wrapping_byte_offset(self, count: isize) -> *const T
let data = [1u8, 2, 3, 4, 5];
let mut ptr: *const u8 = data.as_ptr();
let step = 2;
let end_rounded_up = ptr.wrapping_offset(6);
// 打印 "1, 3, 5, "
while ptr != end_rounded_up {
unsafe {
print!("{}, ", *ptr);
}
ptr = ptr.wrapping_offset(step);
}
pub fn mask(self, mask: usize) -> *const T
// 返回 self 和 origin 之间的元素数量
pub const unsafe fn offset_from(self, origin: *const T) -> isize
// 返回 byte 数量
pub const unsafe fn byte_offset_from<U>(self, origin: *const U) -> isize where U: ?Sized,
let a = [0; 5];
let ptr1: *const i32 = &a[1];
let ptr2: *const i32 = &a[3];
unsafe {
assert_eq!(ptr2.offset_from(ptr1), 2);
assert_eq!(ptr1.offset_from(ptr2), -2);
assert_eq!(ptr1.offset(2), ptr2);
assert_eq!(ptr2.offset(-2), ptr1);
}
// add/sub/sub_ptr() 的关系:
// ptr.sub_ptr(origin) == count
// origin.add(count) == ptr
// ptr.sub(count) == origin
pub unsafe fn sub_ptr(self, origin: *const T) -> usize // 返回两个指针之间的元素数量
pub fn guaranteed_eq(self, other: *const T) -> Option<bool>
pub fn guaranteed_ne(self, other: *const T) -> Option<bool>
// 返回增加 count 个元素后新的地址
pub const unsafe fn add(self, count: usize) -> *const T
pub const unsafe fn byte_add(self, count: usize) -> *const T
let s: &str = "123";
let ptr: *const u8 = s.as_ptr();
unsafe {
println!("{}", *ptr.add(1) as char);
println!("{}", *ptr.add(2) as char);
}
// Calculates the offset from a pointer (convenience for .offset((count as isize).wrapping_neg())).
// count is in units of T; e.g., a count of 3 represents a pointer offset of 3 * size_of::<T>() bytes.
pub const unsafe fn sub(self, count: usize) -> *const T
pub const unsafe fn byte_sub(self, count: usize) -> *const T
pub const fn wrapping_add(self, count: usize) -> *const T
pub const fn wrapping_byte_add(self, count: usize) -> *const T
pub const fn wrapping_sub(self, count: usize) -> *const T
pub const fn wrapping_byte_sub(self, count: usize) -> *const T
// Reads the value from self without moving it. This leaves the memory in self unchanged. See
// ptr::read for safety concerns and examples.
pub const unsafe fn read(self) -> T
pub unsafe fn read_volatile(self) -> T
pub const unsafe fn read_unaligned(self) -> T
// Copies count * size_of<T> bytes from self to dest. The source and destination may
// overlap. NOTE: this has the same argument order as ptr::copy. See ptr::copy for safety concerns and examples.
pub const unsafe fn copy_to(self, dest: *mut T, count: usize)
pub const unsafe fn copy_to_nonoverlapping(self, dest: *mut T, count: usize)
pub fn align_offset(self, align: usize) -> usize
pub fn is_aligned(self) -> bool
pub fn is_aligned_to(self, align: usize) -> bool
*mut T
类型是在 *const T
的方法基础上,增加了一些 write 相关的方法:
impl<T> *mut T where T: ?Sized
// ...
pub unsafe fn as_mut<'a>(self) -> Option<&'a mut T>
pub unsafe fn as_uninit_mut<'a>(self) -> Option<&'a mut MaybeUninit<T>>
// ...
// Executes the destructor (if any) of the pointed-to value. See ptr::drop_in_place for safety concerns and examples.
pub unsafe fn drop_in_place(self)
// Overwrites a memory location with the given value without reading or dropping the old value. See
// ptr::write for safety concerns and examples.
pub unsafe fn write(self, val: T)
pub unsafe fn write_bytes(self, val: u8, count: usize)
pub unsafe fn write_volatile(self, val: T)
pub unsafe fn write_unaligned(self, val: T)
pub unsafe fn replace(self, src: T) -> T
pub unsafe fn swap(self, with: *mut T)
pub fn align_offset(self, align: usize) -> usize
pub fn is_aligned(self) -> bool
pub fn is_aligned_to(self, align: usize) -> bool
std::ptr #
std::ptr module
提供操作 raw pointer
(*const T, *mut T
) 的函数。
通过 *const T
或 *mut T
来存取 T 值,大小一般是 std::mem::size_of::<T>()
字节。
packed struct
:默认情况下,struct field 会被 pading 对齐。通过添加 packed attr
,可以关闭 padding 对齐。
#[derive(Debug, Default, Copy, Clone)]
#[repr(C, packed)]
struct S {
aligned: u8,
unaligned: u32,
}
let s = S::default();
// not allowed with coercion
let p = std::ptr::addr_of!(s.unaligned);
std::mem::offset_of!()
返回 struct field 或 enum variant 的偏移量。
std::ptr::addr_of!(expr)/addr_of_mut!(expr)
宏返回 expr 的 raw pointer,也可以用于未对齐的 raw pointer:
- 这两个宏等效于
&raw const expr,和 &mut raw const expr
。 - 相比
&mut expr as * mut _
的好处是,省去创建了一个引用。 - 未对齐的 field 不能创建引用。
raw pointer
和引用都是指针,包括两部分:
- data pointer:指向 value 内存地址的指针;
- 可选的 metadata 指针;
对于编译时可知的固定大小类型(实现 Sized trait)或 extern 类型的指针,是 thin 指针
,metadata 是零内存的 () 类型,所以 thin 指针
是只占用一个机器字 usize 的变量。
对于动态大小类型(DST),它的指针是 fat 指针
,这时 metadata 非空,如 *const [u8]
或 *mut dyn std::io::Write
:
- 对于最后一个 field 是 DST 的 struct,struct 的 metadata 是最后一个 field 的 metadata;
- 对于 str 类型,metadata 是字符串字节数量(usize);
- 对于 slice 类型,metadata 是元素的数量(usize);
- 对于
trait object
,如dyn SomeTrait
,metadata 是DynMetadata<Self>
(如DynMetadata<dyn SomeTrait>
)
std::ptr::Pointee
trait 为任意指针提供 metadata type 信息, Rust 为所有类型实现了该 trait。其中的 Metadata
关联类型可能是 () 或 usize 或 DynMetadata<_> 类型;
- (): 零大小,对应没有 Metadata 的 thin 指针;
- usize:对应 byte 数量(如
&str
)或 item 数量(如[T]
); - DynMetadata: 对应 trait object,也是个 usize 大小的指针;
std::ptr::to_raw_parts()
返回对象的 data pointer 和 Pointee 对象。
std::ptr::metadata()
方法返回对象的 Metadata 类型对象;
std::ptr::from_raw_parts()/from_raw_parts_mut()
使用 data pointer 和 Metadata 类型对象创建 raw pointer:
pub trait Pointee {
// Metadata 类型可能是:() 或 usize 或 DynMetadata<Dyn: ?Sized>
type Metadata: Copy + Send + Sync + Ord + Hash + Unpin;
}
pub struct DynMetadata<Dyn> where Dyn: ?Sized
// Decompose a (possibly wide) pointer into its data pointer and metadata components.
pub fn to_raw_parts(self) -> (*const (), <T as Pointee>::Metadata)
// Forms a (possibly-wide) raw pointer from a data pointer and metadata.
pub fn from_raw_parts<T>(data_pointer: *const (), metadata: <T as Pointee>::Metadata) -> *const T where T: ?Sized,
std::ptr::DynMetadata
是 trait object
的 metadata,vtable(virtual call table)的指针,指向
trait object 对应的具体值类型的实现,vtable 包括:
- type size
- type alignment
- a pointer to the
type’s drop_in_place impl
(may be a no-op for plain-old-data) - pointers to
all the methods
for the type’s implementation of the trait
Rust 的 type coercion 机制为引用和 raw pointer 提供了隐式自动转换:
&mut T to &T
<- 可变引用可以转换为不可变引用*mut T to *const T
<– 可变 raw pointer 可以转换为不可变 raw pointer&T to *const T
<– 不可变引用 可以转换为 不可变 raw pointer&mut T to *mut T
<– 可变引用 可以转换为 可变 raw pointer
可以使用 as 运算符根据 type coercion 等规则来进行显式转换:
- 规则:https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions
// &mut T -> &T
let r: &i32 = &mut 1i32;
// &T -> *const T
let rp: *const i32 = &1i32;
// &mut T -> *const T 或 *mut T
let r: *const i32 = &mut 1i32;
let r: *mut i32 = &mut 1i32;
// *mut T -> *const T
// 对支持 type coercion 的转换类型,也可以使用 as 运算符显式转换.
let rp: *const i32 = &mut 1i32 as *mut i32 as *const i32;
还有些 unsafe 的转换规则,不在上面文档中, 主要是利用 &MaybeUninit<T>、&mut MaybeUninit<T>、&T、 &mut T、*const T、*mut T
的内存布局、大小、对齐都一致,可以使用 as 表达式相互转换:
// as 表达式支持的转换类型:https://doc.rust-lang.org/reference/expressions/operator-expr.html#r-expr.as.pointer
// 将裸指针转换为 usize
let ptr_num_cast = ptr as *const i32 as usize;
// 将裸指针转换为借用
// 这是因为 &T, &mut T, *const T 和 *mut T 内存布局是一致的,所以可以转换。
let ptr: *mut i32 = &mut 0;
let ref_casted = unsafe { &mut *ptr };
// 将 &mut T 转换为 &mut U:
let ptr = &mut 0;
let val_casts = unsafe { &mut *(ptr as *mut i32 as *mut u32) };
// 将 &[MaybeUninit<u8>] 转换为 *const MaybeUninit<u8>, 然后使用 as 转换为裸指针
let ma = [MaybeUninit<u8>; 32];
let mas: &[MaybeUninit<u8>] = ma;
let mac = unsafe {mas.as_ptr() as *const u8 as *mut u8}
pub const unsafe fn read<T>(src: *const T) -> T
: 创建 src 的一个 bitwise copy 值(返回的 T 和
src 指向同一个内存区域),而不管 T 是否实现了 Copy,不影响和移动 src 内容。如果 T 未实现 Copy,则 src 和返回的 T 由于指向同一个内存区域,
在返回的 T 对象被 drop 后,后续 如果再释放 src 会出现多重释放的问题。
pub unsafe fn write<T>(dst: *mut T, src: T)
:不会 drop dst(有可能导致 dst 内存泄露),将 src 所有权转
移到 dst。
对比: *src = value;
会 Drop src 的值,而 std::ptr::write(dst, src)
在将 src 转移到 dst 中的同时不
drop dst 的值。这点对于通过 raw pointer 操作 MybeUninit 的内存区域很重要(因为未初始化,所以 drop UB)。
let x = 12;
let y = &x as *const i32;
unsafe {
assert_eq!(std::ptr::read(y), 12);
}
fn swap<T>(a: &mut T, b: &mut T) {
unsafe {
// Create a bitwise copy of the value at `a` in `tmp`.
let tmp = std::ptr::read(a);
// Exiting at this point (either by explicitly returning or by calling a
// function which panics) would cause the value in `tmp` to be dropped while
// the same value is still referenced by `a`. This could trigger undefined
// behavior if `T` is not `Copy`.
// Create a bitwise copy of the value at `b` in `a`. This is safe because
// mutable references cannot alias.
std::ptr::copy_nonoverlapping(b, a, 1);
// As above, exiting here could trigger undefined behavior because the same
// value is referenced by `a` and `b`.
// Move `tmp` into `b`.
std::ptr::write(b, tmp);
// `tmp` has been moved (`write` takes ownership of its second argument), so
// nothing is dropped implicitly here.
// tmp 和 a 虽然指向同一个内存区域,但由于 tmp 已经被 write() 转移所有权,所以这里不会释放 tmp,不会有双重释放的问题。
}
}
let mut foo = "foo".to_owned();
let mut bar = "bar".to_owned();
swap(&mut foo, &mut bar);
assert_eq!(foo, "bar");
assert_eq!(bar, "foo");
let mut s = String::from("foo");
unsafe {
// String 未实现 Copy,所以 s2 和 s 共享相同的内存区域
let mut s2: String = ptr::read(&s);
assert_eq!(s2, "foo");
// 向 s2 赋值时,会 Drop 对应内存区域的原始值,所以 s 不能再使用(因其内存已经被释放)
s2 = String::default();
assert_eq!(s2, "");
// Assigning to `s` would cause the old value to be dropped again, resulting in undefined behavior.
// s = String::from("bar"); // ERROR
// ptr::write() 覆盖 s 中的值但不 Drop 它,所以 OK。
ptr::write(&mut s, String::from("bar"));
}
assert_eq!(s, "bar");
异常的情况:String 没有实现 Copy,所以 read() 的 bitwise-copy 机制会将 s 和 spr 都指向同一个内存区域,会导致双重释放,从而导致运 行时出现 abort 终止:
fn main() {
let mut s = "abcd".to_string();
let sp = &mut s as *mut String;
let mut spr = unsafe {std::ptr::read(sp)};
spr.push_str(" test");
println!("s {}, spr {}", s, spr);
}
// 报错:
// Running `target/debug/ptr`
// s abcd, spr abcd test
// ptr(19630,0x1ffb75f00) malloc: *** error for object 0x6000012d4050: pointer being freed was not allocated
// ptr(19630,0x1ffb75f00) malloc: *** set a breakpoint in malloc_error_break to debug
// Abort trap: 6
std::ptr module
提供的其它操作 raw pointer
的函数:
copy<T>(src, dst, count)
: src、dst 内存可以重叠,如果 T 未实现 Copy,则是 bit-copy;copy_nonoverlapping<T>(src, dst, count)
: src、dst 是不能重叠的;drop_in_place<T>(to_drop: *mut T)
: 调用 T 的 drop 来丢弃它,特别适合于 unsized 的trait object
等对象;std::ptr::null()/null_mut()
: 创建一个 null 值的*const T
或*mut T
,等效于:MaybeUninit::<*const T>::zeroed().assume_init()
MaybeUninit::<*mut T>::zeroed().assume_init()
std::ptr::read(src: *const T)-> T
: 从 src 读取 T 值(bit-copy),但是不移动它,src 内存不变;pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize)
:将 dst 开始的count * size_of::<T>()
字节设置为 val。
copy 的 count 用于指定从 src 拷贝 count * size_of::<T>()
字节到 dst。
// Copies count * size_of::<T>() bytes from src to dst. The source and destination may overlap.
// Like read, copy creates a bitwise copy of T, regardless of whether T is Copy.
// If T is not Copy, using both the values in the region beginning at *src and the region beginning
// at *dst can violate memory safety.
pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize)
unsafe fn from_buf_raw<T>(ptr: *const T, elts: usize) -> Vec<T> {
let mut dst = Vec::with_capacity(elts);
// SAFETY: Our precondition ensures the source is aligned and valid,
// and `Vec::with_capacity` ensures that we have usable space to write them.
ptr::copy(ptr, dst.as_mut_ptr(), elts);
// SAFETY: We created it with this much capacity earlier,
// and the previous `copy` has initialized these elements.
dst.set_len(elts);
dst
}
// Copies count * size_of::<T>() bytes from src to dst. The source and destination must not overlap.
pub const unsafe fn copy_nonoverlapping<T>( src: *const T, dst: *mut T, count: usize,)
let p: *const i32 = ptr::null();
assert!(p.is_null());
assert_eq!(p as usize, 0); // this pointer has the address 0
let mut vec = vec![0u32; 4];
unsafe {
let vec_ptr = vec.as_mut_ptr();
ptr::write_bytes(vec_ptr, 0xfe, 2);
}
assert_eq!(vec, [0xfefefefe, 0xfefefefe, 0, 0]);