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() 函数的实现如下:
#[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() 函数的实现如下:
#[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(),
这个函数效率更高.