Rust的借用检查器是否过于复杂化这个切片的例子?

时间:2017-10-08 09:50:58

标签: rust borrow-checker

我目前正在通过重写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 ;程序以及使用借用检查器的重点是什么?

摘要

我错过了一些明显的东西吗?这个问题的简单解决方案是什么?或者这是应该如何完成的?在那种情况下,我无法想象写一些比单片变异程序更重要的东西会是什么。

1 个答案:

答案 0 :(得分:4)

Go示例根本不安全。它对别名内存执行突变。如果将这些切片移动到不同的线程,则可以看到数据竞争。

这意味着Go编译器无法执行基于noalias的优化。另一方面,Rust中的借用检查器确保可变指针没有别名。

  

Rust会创建所有这些安全措施,但它会导致性能下降,因为我们需要多次创建相同的变量,清除它们,将函数保存在内存中等等。

您是否真的观察到这种降级或比较优化的编译器输出?