std::mem::swap有签名:
pub fn swap<T>(x: &mut T, y: &mut T)
Run Code Online (Sandbox Code Playgroud)
如果我尝试实现它(游乐场):
pub fn swap<T>(x: &mut T, y: &mut T)
Run Code Online (Sandbox Code Playgroud)
我收到有关两个参数的生命周期的错误:
pub fn swap<T>(a: &mut T, b: &mut T) {
let t = a;
a = b;
b = t;
}
Run Code Online (Sandbox Code Playgroud)
如果我将签名更改为:
error[E0623]: lifetime mismatch
--> src/lib.rs:4:9
|
1 | pub fn swap<T>(a: &mut T, b: &mut T) {
| ------ ------
| |
| these two types are declared with different lifetimes...
...
4 | b = t;
| ^ ...but data from `a` flows into `b` here
error[E0623]: lifetime mismatch
--> src/lib.rs:3:9
|
1 | pub fn swap<T>(a: &mut T, b: &mut T) {
| ------ ------ these two types are declared with different lifetimes...
2 | let t = a;
3 | a = b;
| ^ ...but data from `b` flows into `a` here
Run Code Online (Sandbox Code Playgroud)
它可以编译,但我收到一条警告,这似乎意味着我们只是交换临时副本:
pub fn swap_lt<'t, T>(mut a: &'t T, mut b: &'t T)
Run Code Online (Sandbox Code Playgroud)
您的代码不在临时副本上运行。它只是交换传入的引用,这对它们指向的值没有任何影响。这也解释了为什么编译器希望生命周期匹配 \xe2\x80\x93 引用指向之前指向的x值引用y,反之亦然,这只有在两个引用具有相同生命周期的情况下才有可能。
当交换实际值时,会出现不同的问题。您首先需要将其中一个值移至临时变量。但是,由于Tis not Copy,因此您无法将值从引用后面移出,因为这会使引用无效,而这在 Rust 中是不允许的。如果您允许T: Default,您可以暂时将该值替换为其默认值。但是,如果要实现一般情况下的功能,则需要诉诸不安全代码。一种方法是使用std::ptr::read()和std::ptr::write()函数从原始指针读取和写入数据:
fn swap<T>(x: &mut T, y: &mut T) {\n unsafe {\n let z = read(x);\n write(x, read(y));\n write(y, z);\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n这段代码比看起来更棘手。该read()函数返回值的副本,而不会使原始值无效,因此我们最终会Copy在两个地方出现相同的非值。我们需要注意不要删除任何值,这在许多情况下会隐式发生。例如,这个实现是错误的,因为它隐式地删除了x最初指向的值
fn swap<T>(x: &mut T, y: &mut T) {\n unsafe {\n let z = read(x);\n *x = read(y); // Wrong \xe2\x80\x93 drops the original value x is pointing to\n write(y, z);\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n标准库中的实际实现swap()使用了一些优化:
它使用std::ptr::copy_nonoverlapping()函数 而不是write(x, read(y)),后者作为编译器内部函数实现。Rust 编译器将其委托给 LLVM,以确保生成的代码对于目标平台尽可能高效。我们的代码实际上使用临时存储来存储和 。使用,仅需要临时存储其中一个变量。xycopy_nonoverlapping()
大小为 32 或更大的值按块交换,因此只需要 32 字节的临时存储。
\n