我目前正在通过重写A Tour of Go的练习来探索Rust的奇迹。
我知道Go和Rust都有不同的功能,并不是所有东西都是完全可重写的,而且我还有与借阅检查器作斗争的一部分。然而,我做了一个相当简单的练习,但我提出的所有解决方案看起来都很复杂。
Go示例
package main
import "fmt"
func main() {
names := [4]string{
"John",
"Paul",
"George",
"Ringo",
}
fmt.Println(names) // [John Paul George Ringo]
a := names[0:2]
b := names[1:3]
fmt.Println(a, b) // [John Paul] [Paul George]
b[0] = "XXX"
fmt.Println(a, b) // [John XXX] [XXX George]
fmt.Println(names) // [John XXX George Ringo]
}
在Go中,我们只创建2个切片,通过一个突变,我们就完成了。由于GC,我们为了简单起见做了一些安全性的权衡。
Rust示例 - #1
fn main() {
let mut names = ["John", "Paul", "George", "Ringo"];
println!("{:?}", names); // [John Paul George Ringo]
{
let a = &names[..2];
let b = &names[1..3];
println!("{:?} {:?}", a, b); // [John Paul] [Paul George]
}
{
// need a separate mutable slice identical to 'b'
let tmp = &mut names[1..3];
tmp[0] = "XXX";
}
{
// need to assign same variables just to print them out
let a = &names[..2];
let b = &names[1..3];
println!("{:?} {:?}", a, b); // [John XXX] [XXX George]
}
println!("{:?}", names); // [John XXX George Ringo]
}
这与前一个示例的一对一重写一样接近,因为我可以得到额外的双重性和开销,所以这远远不是最优的,所以我创建了第二个例子。
Rust示例 - #2
fn slice_writer(arr: &[&str]) {
let a = &arr[..2];
let b = &arr[1..3];
println!("{:?} {:?}", a, b);
}
fn main() {
let mut names = ["John", "Paul", "George", "Ringo"];
println!("{:?}", names);
slice_writer(&names);
{
// still need to have the duplicity of '[1..3]'
let tmp = &mut names[1..3];
tmp[0] = "XXX";
}
slice_writer(&names);
println!("{:?}", names);
}
这写起来真的很麻烦;我需要创建一个单独的函数,以消除分配相同切片的两面性,这是我首先不应该遇到的问题。 Rust会创建所有这些安全措施,但它会导致性能下降,因为我们需要多次创建相同的变量,清除它们,将功能保存在内存中等等,或者我需要使用一些深奥的“不安全”#39 ;程序以及使用借用检查器的重点是什么?
摘要
我错过了一些明显的东西吗?这个问题的简单解决方案是什么?或者这是应该如何完成的?在那种情况下,我无法想象写一些比单片变异程序更重要的东西会是什么。
答案 0 :(得分:4)
Go示例根本不安全。它对别名内存执行突变。如果将这些切片移动到不同的线程,则可以看到数据竞争。
这意味着Go编译器无法执行基于noalias
的优化。另一方面,Rust中的借用检查器确保可变指针没有别名。
Rust会创建所有这些安全措施,但它会导致性能下降,因为我们需要多次创建相同的变量,清除它们,将函数保存在内存中等等。
您是否真的观察到这种降级或比较优化的编译器输出?