Rust如何自定义实现swap交换函数及解决生命周期报错问题
Rust自定义swap函数正确实现方案
错误原因分析
你之前的两次实现都没有抓住swap的核心要求:交换两个可变引用指向的底层内存中的值,而非交换引用本身的指向:
- 第一版实现的问题
- 操作对象是
a、b两个可变引用变量本身,没有解引用操作,根本碰不到底层存储的值 - Rust默认会给两个独立的
&mut T参数分配不同的生命周期,你试图将生命周期不同的引用互相赋值,自然会报生命周期不匹配的错误 - 哪怕你手动给两个参数标注相同的生命周期让编译通过,也只是完成了函数本地引用指向的交换,对外部传入的变量没有任何影响
- 操作对象是
- 第二版修改后的问题
- 主动把
&mut T改成了不可变引用&T,直接丢失了修改底层值的权限 - 签名中的
mut a、mut b只是修饰函数本地的参数变量,修改这两个变量仅作用于函数栈帧,函数退出后修改完全无效,因此编译器会提示赋值未使用的警告
- 主动把
正确实现
1. 带trait约束的安全实现
如果可以接受泛型参数T满足Default约束,可以用完全安全的代码实现:
pub fn swap<T: Default>(a: &mut T, b: &mut T) { let temp = std::mem::take(a); *a = std::mem::take(b); *b = temp; }
该实现借助std::mem::take取出引用指向的值,替换为对应类型的默认值,不需要任何unsafe代码,但是仅支持实现了Default trait的类型。
2. 支持所有类型的通用实现(和标准库行为对齐)
如果需要和标准库的std::mem::swap一样支持任意类型T,需要借助unsafe操作绕开安全Rust的借用规则限制,直接操作裸指针完成内存拷贝:
use std::ptr; pub fn swap<T>(a: &mut T, b: &mut T) { // 两个指针指向同一个地址时不需要交换,避免未定义行为 if ptr::eq(a, b) { return; } // 在栈上申请一块和T大小、对齐一致的未初始化内存作为临时存储 let mut temp = std::mem::MaybeUninit::<T>::uninit(); unsafe { // 将a指向的值拷贝到临时存储 ptr::copy_nonoverlapping(a as *mut T, temp.as_mut_ptr(), 1); // 将b指向的值拷贝到a的位置 ptr::copy_nonoverlapping(b as *mut T, a as *mut T, 1); // 将临时存储中a的旧值拷贝到b的位置 ptr::copy_nonoverlapping(temp.as_ptr(), b as *mut T, 1); } }
注:unsafe块的安全性由代码编写者保证:传入的两个可变引用都是合法、对齐、非悬垂的,且非重叠的情况已经提前判断,因此不会出现未定义行为。
功能验证
可以用以下代码验证swap的正确性:
fn main() { // 验证基础类型 let mut x = 1; let mut y = 2; swap(&mut x, &mut y); assert_eq!(x, 2); assert_eq!(y, 1); // 验证动态长度类型 let mut s1 = String::from("hello"); let mut s2 = String::from("world"); swap(&mut s1, &mut s2); assert_eq!(s1, "world"); assert_eq!(s2, "hello"); }
内容的提问来源于stack exchange,提问作者hkBst
相关产品推荐
相关产品推荐

