使用Vec案例说明Rust所有权概念

22-10-07 banq

Vec与String一样,没有实现Copy特性((String默认是值传递,不是引用传递,也不是值复制):)

Vector(以及其他的集合)是值得讨论的,因为涉及到很多语义--容器本身,元素,以及迭代器。

我们在这里使用Vec作为例子,因为它是一个非常常见的数据结构的集合。其他也没有实现Copy特性的集合包括HashMap和HashSet。另一方面,数组的移动语义与结构类似,它们取决于项目的类型--但这是另一个问题。

fn main() {
    let names = vec![
          String::from("John"),
          String::from("Jane"),
      ];
    do_something(names);
}

fn do_something(names: Vec<String>) {
    println!("{:?}", names);
}


输出:

["John", "Jane"]


但是,当我们在do_something后面添加println! 语句。

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
      ];
    do_something(names);
    println!("{:?}", names);
}

fn do_something(names: Vec<String>) {
    println!("{:?}", names);
}


我们以前见过这种编译器错误,对于String类型,我们试图借用/使用一个已经被移动过的值:

error[E0382]: borrow of moved value: `names`
 --> src/main.rs:7:22


我们可以先借用names,这样我们就可以在事后把它移到do_something函数中,两行代码互换一下

fn main() {
    let names = vec![
          String::from("John"),
          String::from("Jane"),
      ];
    println!("{:?}", names);
    do_something(names);
}

fn do_something(names: Vec<String>) {
    println!("{:?}", names);
}


输出:

["John", "Jane"]
<p class="indent">["John", "Jane"]


我们可以重新设计do_something来借用names,这样我们就可以在println时再次借用它!。

fn main() {
    let names = vec![
          String::from("John"),
          String::from("Jane"),
      ];
    do_something(&names);
    println!("{:?}", names);
}

fn do_something(names: &[String]) {
    println!("{:?}", names);
}


下面的代码在我知道的编程语言中非常好,但是在这里,我们得到一个编译器错误:

fn main() {
    let names = vec![
          String::from("John"),
          String::from("Jane"),
      ];
    let name: String = names[0];
    println!("Hello, {}", name);
}


报错:

error[E0507]: cannot move out of index of `Vec<String>`
 --> src/main.rs:6:24


,如果你只从Vec中移出一个元素,你就会让向量处于无效状态--向量不再是一个同质元素的集合了

隐含地移出一个Vec是不允许的,因为这将使向量处于无效状态--一个元素被移出,其他元素没有一致性了:
如果我对这个向量进行迭代,我可能会访问一个无效的内存(被移出的元素)。

那么我们该怎么做呢?

我们可以通过使用索引操作符借用我们想要的值。

fn main() {
    let names = vec![
          String::from("John"),
          String::from("Jane"),
      ];
    let name: &str = &names[0];
    println!("Hello, {}", name);
}


我们也可以使用.get方法借用该值。

fn main() {
    let names = vec![
          String::from("John"),
          String::from("Jane"),
      ];
    let name: &str = 
          names.get(0).unwrap();
    println!("Hello, {}", name);
}


我们可以用.first()或.last()方法借用这个值(当然,如果你想要第一项和最后一项)。

fn main() {
    let names = vec![
          String::from("John"),
          String::from("Jane"),
      ];
    let name: &str = 
          names.first().unwrap();
    println!("Hello, {}", name);
}


但是......如果我想拥有一个元素呢?

我们使用.into_iter()方法来拥有各个元素。在这里,我们在调用.next()后设法拥有了第一个元素。

fn main() {
    let names = vec![
          String::from("John"),
          String::from("Jane"),
      ];
    let mut names_iter = 
          names.into_iter();
    let name: String = 
          names_iter.next().unwrap();
    println!("Hello, {}", name);
}


如果你想拥有第一个元素,这个例子是有效的。我想,如果你想拥有索引为n的元素(出于某种原因),你可以调用.skip(n)然后调用.next()。

对于Vec,我们可以.pop()最后一个元素并拥有数据(当然,只有当你想要一个向量的最后一个元素的时候)。

fn main() {
    let mut names = vec![
          String::from("John"),
          String::from("Jane"),
      ];
    let name: String = names.pop()
          .unwrap();
    println!("Hello, {}", name);
}