用Iterator解释Rust所有权概念

22-10-07 banq

当涉及到集合中元素的所有权时,迭代器起着极其重要的作用。
在下面这些例子中,我们将使用Vec<String>,故意使用String作为元素(它没有实现Copy trait:String默认是值传递,不是引用传递,也不是值复制),这样我们就可以在向量中演示其移动语义。

让我们从一个对names进行迭代的for-loop开始。为什么是for-loop?我们后面将讨论这个问题。

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


但是,当直到我们在for-loop下面添加一个print语句......在那里编译器开始抱怨。

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

编译器:

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


显然,在循环之前有一个 "隐含的.into_iter调用"。

等等,隐式?为什么呢!?但是,好吧,让我们添加那个隐式的.into_iter。

在加入into_iter()之后,我们预计程序仍然不能编译。

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

    for name in names.into_iter() {
        println!("{}", name);
    }

    println!("Names: {:?}", names);
}


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


嗯,让我们给names.into_iter()分配一个变量。这样我们就可以直观地看到这个动作。请注意,我们仍然希望程序不被编译。

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];
  
    let names_iter = names.into_iter();
    for name in names_iter {
        println!("{}", name);
    }
    println!("Names: {:?}", names);
}


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


发生的情况是,使用 into_iter,我们正在将元素从 names 移到 names_iter。结果是,我们不能再使用names了!

我们该怎么做呢?

我们可以使用.iter()从名字中借用元素:

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

    for name in names.iter() {
        println!("{}", name);
    }

    println!("Names: {:?}", names);
}


正常输出:

John
Jane
Names: ["John", "Jane"]


我们可以使用一个slice(实现了IntoIterator):

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

    for name in &names {
        println!("{}", name);
    }

    println!("Names: {:?}", names);
}

正常输出

如果你的程序允许,我们可以把代码行调换一下,先借用名字,然后再移动它。

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

    println!("Names: {:?}", names);

    for name in names {
        println!("{}", name);
    }
}


我们也可以克隆该vector。请注意,克隆是有成本的。

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

    for name in names.clone() {
        println!("{}", name);
    }

    println!("Names: {:?}", names);
}



函数式编程
使用函数式编程成语使得当你想对元素进行迭代时,迭代器的作用更加明显(尤其是当你来自于不用直接处理迭代器的编程语言时)。

与for-loop不同,没有隐含的.into_iter()调用。因此,对于向量,我们总是需要调用.iter()、.into_iter()等来获得一个迭代器。

下面代码不能编译,因为我们需要对元素进行迭代,这意味着我们需要使用例如.iter()获得一个迭代器。

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


error[E0599]: `Vec<String>` is not an iterator
 --> src/main.rs:7:11



这里,我们使用了.iter()和.for_each()的组合。

迭代器返回一个元素的引用。我们仍然可以在之后使用names对象。

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];
    
    names
        .iter()
        .for_each(|name|
              println!("{}", name));
    
    println!("{:?}", names);
}


输出:

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



如果你想移动这些元素,可以使用.into_iter()。然而,请注意,之后我们就不能使用这个向量了。

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];
  
    names
        .into_iter()
        .for_each(|name|
              println!("{}", name));
  
    println!("{:?}", names); 
}


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


你需要重新设计你的程序,使你不使用被移动的元素。这里,我们把打印语句移到了前面。

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

    names
        .into_iter()
        .for_each(|name|
              println!("{}", name));
}



我们也可以在调用.into_iter()之前克隆该向量。

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

    names.clone()
        .into_iter()
        .for_each(|name|
              println!("{}", name));
    
    println!("{:?}", names); 
}