ptr::swap() 与 ptr::replace() 函数
这两个函数的定义如下:
pub unsafe fn swap<T>(x: *mut T, y: *mut T);
pub unsafe fn replace<T>(dst: *mut T, src: T) -> T;
- swap() 用于交换两个指针
- replace() 用于交dest 指向 src 对象的地址, 并返回原先的值
使用这两个函数, 要满足必要条件:
- src/dst 要是有效的地址, 可读可写
- src/dst 要被初始化
- src/dst 要内存对齐
看一下示例代码:
use std::ptr; fn main() { let mut msg = ['b', 'u', 's', 't']; let c = unsafe { ptr::replace(&mut msg[0], 'r') }; assert_eq!(msg[0], 'r'); assert_eq!(c, 'b'); let mut msg2 = ['b', 'u', 's', 't']; let mut c2 = 'r'; unsafe { ptr::swap(&mut msg2[0], &mut c2); } assert_eq!(msg2[0], 'r'); assert_eq!(c2, 'b'); }
replace() 的实现
replace() 函数的实现如下:
#![allow(unused)] fn main() { #[inline] pub const unsafe fn replace<T>(dst: *mut T, src: T) -> T { unsafe { ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::replace requires that the pointer argument is aligned and non-null", ( addr: *const () = dst as *const (), align: usize = align_of::<T>(), ) => ub_checks::is_aligned_and_not_null(addr, align) ); mem::replace(&mut *dst, src) } } }
这个函数会先检查一下代码是否对齐, 然后就直接调用 mem::replace()
来交换两个内存地址.
swap() 的实现
swap() 函数的实现如下:
#![allow(unused)] fn main() { #[inline] pub const unsafe fn swap<T>(x: *mut T, y: *mut T) { // Give ourselves some scratch space to work with. // We do not have to worry about drops: `MaybeUninit` does nothing when dropped. let mut tmp = MaybeUninit::<T>::uninit(); // Perform the swap // SAFETY: the caller must guarantee that `x` and `y` are // valid for writes and properly aligned. `tmp` cannot be // overlapping either `x` or `y` because `tmp` was just allocated // on the stack as a separate allocated object. unsafe { copy_nonoverlapping(x, tmp.as_mut_ptr(), 1); copy(y, x, 1); // `x` and `y` may overlap copy_nonoverlapping(tmp.as_ptr(), y, 1); } } }
可以看到, 它分成了以下几步:
- 在栈上分配一个临时对象 tmp
- 将目标对象 dst 拷贝到 tmp
- 将源对象 src 拷贝到 dst
- 最后将 tmp 拷贝到 src
可以发现这个步骤比较多, 如果 src
与 dst
的内存没有重叠, 可以使用 swap_nonoverlapping(),
这个函数效率更高.