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);
    }
}
}

可以看到, 它分成了以下几步:

  1. 在栈上分配一个临时对象 tmp
  2. 将目标对象 dst 拷贝到 tmp
  3. 将源对象 src 拷贝到 dst
  4. 最后将 tmp 拷贝到 src

可以发现这个步骤比较多, 如果 srcdst 的内存没有重叠, 可以使用 swap_nonoverlapping(), 这个函数效率更高.