在 Rust 中是否有循环旋转可变变量的标准方法?
Is there a standard way of cyclically rotating mutable variables in Rust?
实现算法中一个非常常见的操作是循环旋转:给定,比方说,3 个变量 a、b、c 将它们更改为
t ⇽ c
c ⇽ b
b ⇽ a
a ⇽ t
考虑到一切都是按位交换的,循环旋转应该是 Rust 比我所知道的任何其他语言都更胜一筹的领域。
相比之下,在 C++ 中,旋转 N 个元素最有效的通用方法是执行 n+1 std::move
操作,这反过来大致导致(对于典型的移动构造函数实现)3 (n+1) sizeof(T)
单词分配(这可以通过模板专门化旋转为 PODs 改进,但需要工作)。
在 Rust 中,该语言可以仅使用 (n+1) size_of(T)
个单词赋值来实现旋转。令我惊讶的是,我找不到标准库支持旋转。 (std::mem
中没有旋转方法)。它可能看起来像这样:
pub fn rotate<T>(x: &mut T, y: &mut T, z: &mut T) {
unsafe {
let mut t: T = std::mem::uninitialized();
std::ptr::copy_nonoverlapping(&*z, &mut t, 1);
std::ptr::copy_nonoverlapping(&*y, z, 1);
std::ptr::copy_nonoverlapping(&*x, y, 1);
std::ptr::copy_nonoverlapping(&t, x, 1);
std::mem::forget(t);
}
}
为了阐明为什么不能在 C++ 中有效地实现旋转,请考虑:
struct String {
char *data1;
char *data2;
String(String &&other) : data1(other.data1), data2(other.data2)
{ other.data1 = other.data2 = nullptr;}
String &operator=(String &&other)
{ std::swap(data1, other.data1); std::swap(data2, other.data2);
return *this; }
~String() { delete [] data1; delete [] data2; }
};
这里像 s2 = std::move(s1);
这样的操作将为每个成员字段进行 3 次指针赋值,总共需要 6 次赋值,因为指针交换需要 3 次赋值(1 次进入 temp,1 次 out temp,1 次跨操作数)
Is there a standard way of cyclically rotating mutable variables in Rust?
没有
我只是交换变量两次,不需要 unsafe
:
use std::mem;
pub fn rotate<T>(x: &mut T, y: &mut T, z: &mut T) {
mem::swap(x, y);
mem::swap(y, z);
}
fn main() {
let mut a = 1;
let mut b = 2;
let mut c = 3;
println!("{}, {}, {}", a, b, c);
// 1, 2, 3
rotate(&mut a, &mut b, &mut c);
println!("{}, {}, {}", a, b, c);
// 2, 3, 1
}
这会产生 7 movl
条指令(Rust 1.35.0,发布,x86_64,Linux)
playground::rotate:
movl (%rdi), %eax
movl (%rsi), %ecx
movl %ecx, (%rdi)
movl %eax, (%rsi)
movl (%rdx), %ecx
movl %ecx, (%rsi)
movl %eax, (%rdx)
retq
相对于最初的 6 movl
条指令:
playground::rotate_original:
movl (%rdx), %eax
movl (%rsi), %ecx
movl %ecx, (%rdx)
movl (%rdi), %ecx
movl %ecx, (%rsi)
movl %eax, (%rdi)
retq
我可以放弃那条指令,转而使用更容易推理的纯安全代码。
在 "real" 代码中,我将利用所有变量都是同一类型并且 slice::rotate_left
and slice::rotate_right
存在的事实:
fn main() {
let mut vals = [1, 2, 3];
let [a, b, c] = &vals;
println!("{}, {}, {}", a, b, c);
// 1, 2, 3
vals.rotate_left(1);
let [a, b, c] = &vals;
println!("{}, {}, {}", a, b, c);
// 2, 3, 1
}
实现算法中一个非常常见的操作是循环旋转:给定,比方说,3 个变量 a、b、c 将它们更改为
t ⇽ c
c ⇽ b
b ⇽ a
a ⇽ t
考虑到一切都是按位交换的,循环旋转应该是 Rust 比我所知道的任何其他语言都更胜一筹的领域。
相比之下,在 C++ 中,旋转 N 个元素最有效的通用方法是执行 n+1 std::move
操作,这反过来大致导致(对于典型的移动构造函数实现)3 (n+1) sizeof(T)
单词分配(这可以通过模板专门化旋转为 PODs 改进,但需要工作)。
在 Rust 中,该语言可以仅使用 (n+1) size_of(T)
个单词赋值来实现旋转。令我惊讶的是,我找不到标准库支持旋转。 (std::mem
中没有旋转方法)。它可能看起来像这样:
pub fn rotate<T>(x: &mut T, y: &mut T, z: &mut T) {
unsafe {
let mut t: T = std::mem::uninitialized();
std::ptr::copy_nonoverlapping(&*z, &mut t, 1);
std::ptr::copy_nonoverlapping(&*y, z, 1);
std::ptr::copy_nonoverlapping(&*x, y, 1);
std::ptr::copy_nonoverlapping(&t, x, 1);
std::mem::forget(t);
}
}
为了阐明为什么不能在 C++ 中有效地实现旋转,请考虑:
struct String {
char *data1;
char *data2;
String(String &&other) : data1(other.data1), data2(other.data2)
{ other.data1 = other.data2 = nullptr;}
String &operator=(String &&other)
{ std::swap(data1, other.data1); std::swap(data2, other.data2);
return *this; }
~String() { delete [] data1; delete [] data2; }
};
这里像 s2 = std::move(s1);
这样的操作将为每个成员字段进行 3 次指针赋值,总共需要 6 次赋值,因为指针交换需要 3 次赋值(1 次进入 temp,1 次 out temp,1 次跨操作数)
Is there a standard way of cyclically rotating mutable variables in Rust?
没有
我只是交换变量两次,不需要 unsafe
:
use std::mem;
pub fn rotate<T>(x: &mut T, y: &mut T, z: &mut T) {
mem::swap(x, y);
mem::swap(y, z);
}
fn main() {
let mut a = 1;
let mut b = 2;
let mut c = 3;
println!("{}, {}, {}", a, b, c);
// 1, 2, 3
rotate(&mut a, &mut b, &mut c);
println!("{}, {}, {}", a, b, c);
// 2, 3, 1
}
这会产生 7 movl
条指令(Rust 1.35.0,发布,x86_64,Linux)
playground::rotate:
movl (%rdi), %eax
movl (%rsi), %ecx
movl %ecx, (%rdi)
movl %eax, (%rsi)
movl (%rdx), %ecx
movl %ecx, (%rsi)
movl %eax, (%rdx)
retq
相对于最初的 6 movl
条指令:
playground::rotate_original:
movl (%rdx), %eax
movl (%rsi), %ecx
movl %ecx, (%rdx)
movl (%rdi), %ecx
movl %ecx, (%rsi)
movl %eax, (%rdi)
retq
我可以放弃那条指令,转而使用更容易推理的纯安全代码。
在 "real" 代码中,我将利用所有变量都是同一类型并且 slice::rotate_left
and slice::rotate_right
存在的事实:
fn main() {
let mut vals = [1, 2, 3];
let [a, b, c] = &vals;
println!("{}, {}, {}", a, b, c);
// 1, 2, 3
vals.rotate_left(1);
let [a, b, c] = &vals;
println!("{}, {}, {}", a, b, c);
// 2, 3, 1
}