黎 飞

黎 飞

1661542200

Rust 所有权:50 个代码示例

遇到所有权问题,例如借用已经转移的价值?如果你是,那么我有 50 个代码片段(好吧,有 53 个)供我们在 Rust Playground 中一起练习。

本文假设您大致了解基本类型,如、 、、迭代器、切片和结构等String整数类型。我假设你已经阅读了Rust 编程语言(尤其是第 4 章:理解所有权)和Rust by Example书籍。i32Vec

这些示例以简单类型(以 开头String)开始,然后移至稍微更“复杂”的类型。✅ 表示代码可以编译,❌ 表示不能。虽然一些示例建议了无法编译的代码的替代方案,但请注意,这些选项并不详尽,仅适用于初学者。这些示例不包括可变性(因为我觉得它们更容易掌握)并且不处理所有权异步编程。

1.字符串

AString是一种不实现Copy特征的类型。

✅ 清单 1–1

让我们用它创建一个Stringthen 调用do_something

一切看起来都很正常……

fn main() {
    let name = String::from("Rust");
    do_something(name);
}

fn do_something(name: String) {
    println!("Hello, {}!", name);
}

清单 1-1。在这里看游乐场

.Hello, Rust!

❌ 清单 1-2

让我们在第 4 行添加一个println!语句……

fn main() {
    let name = String::from("Rust");
    do_something(name);
    println!("{}", name);
}

fn do_something(name: String) {
    println!("Hello, {}!", name);
}

清单 1-2。在这里看游乐场

.error[E0382]: borrow of moved value: `name`
 --> src/main.rs:4:20

这是非法的!这是因为我们试图使用(借用,在 的情况下println!name已经do_something在第 3 行移入的值。

您不能使用已移动的值。在namecan reach之前,它在函数结束时println!被移入do_something超出范围。

我们能做什么?

✅ 清单 1-3

我们可以做的是将 name 克隆为name_clone,这样我们将 name 用于第 5 行和name_clone第 6 行。请注意,克隆是有成本的。

fn main() {
    let name = String::from("Rust");
    let name_clone = name.clone();

    do_something(name);
    println!("{}", name_clone);
}

fn do_something(name: String) {
    println!("Name: {}!", name);
}

清单 1-3。看这里的操场。

Name: Rust!
Rust

✅ 清单 1-4

或者我们可以在第 3 行中通过引用传递namedo_something,并且仍然可以println!在第 4 行中使用它。

fn main() {
    let name = String::from("Rust");
    do_something(&name);
    println!("{}", name);
}

fn do_something(name: &str) {
    println!("Hello, {}!", name);
}

清单 1-4。看这里的操场。

Hello, Rust!
Rust

✅ 清单 1-5

或者,如果您的程序允许,我们可以简单地切换一下代码。

在这里,我们首先借用name,然后将其移入do_something。该程序可以编译,因为在该do_something行之后,main范围内没有其他人将使用name.

fn main() {
    let name = String::from("Rust");
    println!("{}", name);
    do_something(name);
}

fn do_something(name: String) {
    println!("Hello, {}!", name);
}

清单 1-5。看这里的操场。

Rust
Hello, Rust!

 

2. i32

Copy对于实现trait的类型来说,生活要容易得多,比如i32.

✅ 清单 2–1

再次,让我们从简单的事情开始。我们创建age并调用do_something它。在这里,age复制了 的值,因为该i32类型实现了Copytrait。

fn main() {
    let age: i32 = 25;
    do_something(age);
}

fn do_something(age: i32) {
    println!("Hello, {}!", age);
}

清单 2-1。看这里的操场。

Hello, 25!

✅ 清单 2–2

的值age在第 4 行再次复制。

fn main() {
    let age: i32 = 25;
    do_something(age);
	println!("{}", age);
}

fn do_something(age: i32) {
    println!("Hello, {}!", age);
}

清单 2-2。看这里的操场。

Hello, 25!
25

✅ 清单 2–3

我们也可以通过引用传递值,尽管我认为这不是惯用的。

fn main() {
    let age: i32 = 25;
    do_something(&age);
    println!("{}", age);
}

fn do_something(age: &i32) {
    println!("Hello, {}!", age);
}

清单 2-3。看这里的操场。

Hello, 25!
25

✅ ⚠️ 清单 2-4

在第 3 行中,我们首先进行了克隆age,然后将其传递给do_something. 程序会编译,但请注意复制将进行两次——一次在 中,另一次是在进入作用域.clone()时进行复制。agedo_something

请注意,应避免使用此成语。请参阅此处的clone_on_copy 上的 Clippy lint 。

fn main() {
    let age: i32 = 25;
    do_something(age.clone());
    println!("{}", age);
}

fn do_something(age: i32) {
    println!("Hello, {}!", age);
}

清单 2-4。看这里的操场。

Hello, 25!
25

 

3. 带String字段的结构

如果一个类型Copy的所有组件都实现了,那么它就可以实现Copy(请参阅我的类型何时可以是Copy)。

在这些清单中,我们关注的是由一个实现的字段组成的Movie结构。结果,无法实施。StringCopyMovie Copy

✅ 清单 3–1

到目前为止看起来不错,不是吗?程序编译(忽略未使用的字段警告),我们都很高兴。

#[derive(Debug)]
struct Movie {
    title: String,
}
fn main() {
    let movie = Movie { 
        title: String::from("Rust")
    };
    do_something(movie);
}
fn do_something(movie: Movie) {
    println!("Movie: {:?}!", movie);
}

清单 3-1。看这里的操场。

Movie: Movie { title: "Rust" }!

❌ 清单 3-2

但是在添加之后println!,编译器抱怨我们试图借用一个movie已经移入的值do_something

String我们在前面的清单中已经看到过这种移动语义 for 's 。

#[derive(Debug)]
struct Movie {
    title: String,
}
fn main() {
    let movie = Movie { 
        title: String::from("Rust")
    };
    do_something(movie);
    println!("Movie: {:?}", movie);
}
fn do_something(movie: Movie) {
    println!("Movie: {:?}!", movie);
}

清单 3-2。看这里的操场。

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

我们做什么?

✅ 清单 3–3

我们可以借用movie而不是移动它。

#[derive(Debug)]
struct Movie {
    title: String,
}
fn main() {
    let movie = Movie { 
        title: String::from("Rust")
    };
    do_something(&movie);
    println!("Movie: {:?}", movie);
}
fn do_something(movie: &Movie) {
    println!("Movie: {:?}!", movie);
}

清单 3-3。看这里的操场。

Movie: Movie { title: "Rust" }!
Movie: Movie { title: "Rust" }

✅ 清单 3–4

或者我们可以克隆moviedo_something. 这需要Movie实施Clone

#[derive(Debug, Clone)]
struct Movie {
    title: String,
}
fn main() {
    let movie = Movie { 
        title: String::from("Rust")
    };
    do_something(movie.clone());
    println!("Movie: {:?}", movie);
}
fn do_something(movie: Movie) {
    println!("Movie: {:?}!", movie);
}

清单 3-4。看这里的操场。

Movie: Movie { title: "Rust" }!
Movie: Movie { title: "Rust" }

✅ 清单 3–5

或者,如果您的程序允许,我们可以在不克隆的情况下稍微切换代码。在这里,借来movieprintln!然后movie搬进do_something

#[derive(Debug)]
struct Movie {
    title: String,
}
fn main() {
    let movie = Movie { 
        title: String::from("Rust")
    };
    println!("Movie: {:?}", movie);
    do_something(movie);
}
fn do_something(movie: Movie) {
    println!("Movie: {:?}!", movie);
}

清单 3-5。看这里的操场。

Movie: Movie { title: "Rust" }
Movie: Movie { title: "Rust" }!

 

4.带有u8字段的结构体

如前所述,Copy如果一个类型的所有组件都实现了,则该类型可以实现Copy

在这些清单中,我们关注的是由一个实现特征的字段组成的Book结构。结果,可以实施。u8CopyMovie Copy

✅ 清单 4–1

在这个清单中,一切看起来都很正常,对吧?

#[derive(Debug)]
struct Book {
    id: u8,
}
fn main() {
    let book = Book { id: 1 };
    do_something(book);
}
fn do_something(book: Book) {
    println!("Book: {:?}!", book);
}

清单 4-1。看这里的操场。

Book: Book { id: 1 }!

❌ 清单 4-2

直到我们在println!下面添加声明do_something。发生了什么?

因为book没有实现Copytrait(或者还没有实现),Rust进入 . 但是……因为被移动了,不能再使用这个值了。bookdo_somethingbookprintln!

#[derive(Debug)]
struct Book {
    id: u8,
}
fn main() {
    let book = Book { id: 1 };
    do_something(book);
    println!("Book: {:?}", book);
}
fn do_something(book: Book) {
    println!("Book: {:?}!", book);
}

清单 4-2。看这里的操场。

error[E0382]: borrow of moved value: `book`
 --> src/main.rs:8:28

我们能做什么?

❌ 清单 4-3

一种方法是Copy通过派生来实现 Book 的特征Copy

#[derive(Debug, Copy)]
struct Book {
    id: u8,
}
fn main() {
    let book = Book { id: 1 };
    do_something(book);
    println!("Book: {:?}", book);
}
fn do_something(book: Book) {
    println!("Book: {:?}!", book);
}

清单 4-3。看这里的操场。

error[E0277]: the trait bound `Book: Clone` is not satisfied
 --> src/main.rs:1:17

但是……我们仍然得到一个错误——特征绑定Book: Clone不满足。

✅ 清单 4–4

如果我们转到docs,它说所有必须实现的东西也Copy必须实现Clone,因为Clone它是一个超特征。

因此,让我们在现有特征之上实现Clone特征。BookCopy

#[derive(Debug, Copy, Clone)]
struct Book {
    id: u8,
}
fn main() {
    let book = Book { id: 1 };
    do_something(book);
    println!("Book: {:?}", book);
}
fn do_something(book: Book) {
    println!("Book: {:?}!", book);
}

清单 4-4。看这里的操场。

Book: Book { id: 1 }!
Book: Book { id: 1 }

✅ 清单 4–5

甜的!我们还可以重新设计我们的程序,以便改为do_something借用。book

#[derive(Debug)]
struct Book {
    id: u8,
}
fn main() {
    let book = Book { id: 1 };
    do_something(&book);
    println!("Book: {:?}", book);
}
fn do_something(book: &Book) {
    println!("Book: {:?}!", book);
}

清单 4-5。看这里的操场。

Book: Book { id: 1 }!
Book: Book { id: 1 }

 

5. 带有两个字符串字段的结构

可以将值移出结构吗?🤔

在本节中,我们将讨论部分移动

✅ 清单 5–1

到目前为止一切看起来都很正常……

#[derive(Debug)]
struct Person {
    name: String,
    alias: String,
}
fn main() {
    let person = Person { 
        name: "John".to_string(),
        alias: "Johan".to_string(),
    };
    print_alias(person.alias);
}
fn print_alias(alias: String) {
    println!("Person: {:?}!", alias);
}

清单 5-1。看这里的操场。

Person: "Johan"!

❌ 清单 5-2

然而,在这个清单中,我们在print_alias.

该程序没有编译,因为person.alias已经部分移入print_alias但我们尝试在该行person中整体借用。println!

#[derive(Debug)]
struct Person {
    name: String,
    alias: String,
}
fn main() {
    let person = Person { 
        name: "John".to_string(),
        alias: "Johan".to_string(),
    };
    print_alias(person.alias);
    println!("{:?}", person);
}
fn print_alias(alias: String) {
    println!("Person: {:?}!", alias);
}

清单 5-2。看这里的操场。

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

我们能做什么?有哪些合法举措?

事实证明,我们可以部分移动字段——只要确保我们以后不再尝试使用结构本身。

在这里,我们将这两个字段移动到两个不同的函数中。

#[derive(Debug)]
struct Person {
    name: String,
    alias: String,
}

fn main() {
    let person = Person { 
        name: "John".to_string(),
        alias: "Johan".to_string(),
    };
    print_alias(person.alias);
    print_name(person.name);
}

fn print_alias(alias: String) {
    println!("Alias: {:?}!", alias);
}

fn print_name(name: String) {
    println!("Name: {:?}!", name);
}

清单 5-3。看这里的操场。

Alias: "Johan"!
Name: "John"!

✅ 清单 5–4

我们可以通过引用传递这些字段。

#[derive(Debug)]
struct Person {
    name: String,
    alias: String,
}

fn main() {
    let person = Person { 
        name: "John".to_string(),
        alias: "Johan".to_string(),
    };
    print_alias(&person.alias);
    print_name(&person.name);
}

fn print_alias(alias: &str) {
    println!("Alias: {:?}!", alias);
}

fn print_name(name: &str) {
    println!("Name: {:?}!", name);
}

清单 5-4。看这里的操场。

Alias: "Johan"!
Name: "John"!

✅ 清单 5–5

或者如果我们以后想person再次使用,我们可以克隆这些值。

#[derive(Debug)]
struct Person {
    name: String,
    alias: String,
}

fn main() {
    let person = Person { 
        name: "John".to_string(),
        alias: "Johan".to_string(),
    };
    print_alias(person.alias.clone());
    print_name(person.name.clone());
    println!("{:?}", person);
}

fn print_alias(alias: String) {
    println!("Alias: {:?}!", alias);
}

fn print_name(name: String) {
    println!("Name: {:?}!", name);
}

清单 5-5。看这里的操场。

Alias: "Johan"!
Name: "John"!
Person { name: "John", alias: "Johan" }

✅ 清单 5–6

或者你可以移动东西而不必克隆。在这里,我们首先借用了person,然后部分移动了这些字段。

#[derive(Debug)]
struct Person {
    name: String,
    alias: String,
}

fn main() {
    let person = Person { 
        name: "John".to_string(),
        alias: "Johan".to_string(),
    };
    println!("{:?}", person);
    print_alias(person.alias);
    print_name(person.name);
}

fn print_alias(alias: String) {
    println!("Alias: {:?}!", alias);
}

fn print_name(name: String) {
    println!("Name: {:?}!", name);
}

清单 5-6。看这里的操场。

Person { name: "John", alias: "Johan" }
Alias: "Johan"!
Name: "John"!

 

6. Vec<字符串>

String, Vec's 被移动了,因为它们没有实现Copy特征(见这里)。相同的移动语义适用。

向量(以及与此相关的其他集合)值得讨论,因为它涉及到很多语义——容器本身、元素和迭代器。

我们Vec在这里用作示例,因为它是集合中非常常见的数据结构。其他也没有实现该Copy特征的集合包括HashMapHashSet。另一方面,数组的移动语义与结构类似,因为它们取决于项目的类型——但那是另一天的事了。

✅ 清单 6–1

与往常一样,我们将从快乐的事情开始:

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

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

清单 6-1。看这里的操场。

["John", "Jane"]

❌ 清单 6–2

直到我们println!do_something.

String对于我们在移动后尝试借用/使用值的类型,我们之前已经看到过这个编译器错误。

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

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

清单 6-2。看这里的操场。

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

我们能做什么?

✅ 清单 6–3

我们可以先借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);
}

清单 6-3。看这里的操场。

["John", "Jane"]
["John", "Jane"]

✅ 清单 6–4

我们可以重新设计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);
}

清单 6-4。看这里的操场。

["John", "Jane"]
["John", "Jane"]

✅ 清单 6–5

最后,我们可以克隆 的向量do_something。请注意,向量克隆是浅拷贝。

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

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

清单 6-5。看这里的操场。

["John", "Jane"]
["John", "Jane"]

通过获取索引来读取向量中的元素是很棘手的。只是在我们所知道的编程语言中不一样。

❌ 清单 6–6

我的意思是你会看看这个!下面的代码在我知道的编程语言中非常好,但是在这里,我们得到一个编译器错误,说我不能移出Vec<String>.

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

清单 6-6。在这里看游乐场

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

但为什么?

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

Vec不允许隐式移出 a ,因为它会使向量处于无效状态——一个元素被移出,其他元素则不被移出(请参阅StackOverflow 帖子)。如果我要迭代这个向量,我可能会访问一个无效的内存(被移出的元素)😱。谢谢你保护我们,Rust。

那么我们该怎么办?

✅ 清单 6–7

我们可以使用索引运算符借用我们想要的值。

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

清单 6-7。看这里的操场。

Hello, John

✅ 清单 6–8

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

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

清单 6-8。看这里的操场。

Hello, John

✅ 清单 6–9

.first()我们可以使用or方法借用该值.last()(当然,如果您想要第一项和最后一项)。

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

清单 6-9。看这里的操场。

Hello, John

但是……如果我想拥有一个元素怎么办?

✅ 清单 6–10

我们使用该.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);
}

清单 6-10。看这里的操场。

Hello, John

如果您想拥有第一个元素,则此示例有效。Afaik,如果你想拥有索引n处的元素(出于某种原因),你会调用.skip(n)then call .next()

✅ 清单 6–11

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

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

清单 6-11。看这里的操场。

Hello, Jane

 

7. 迭代器

当涉及到集合中元素的所有权时,迭代器扮演着极其重要的角色。

在这些示例中,我们将使用Vec<String>,有意地String用作元素(它不实现Copy特征),以便我们可以在向量中展示其移动语义。

for循环

✅ 清单 7–1

让我们从一个 for 循环迭代开始names。为什么是for循环?我们会解决的。

但就目前而言,生活是美轮美奂的:

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

清单 7-1。看这里的操场。

John
Jane

❌ 清单 7–2

直到我们在 for 循环下面添加一个 print 语句……编译器开始抱怨。

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

清单 7-2。看这里的操场。

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

显然有一个“.into_iter循环前的隐式调用”。

等等,隐含的?为什么?!但是好的,让我们添加隐含的.into_iter

❌ 清单 7-3

添加后into_iter(),我们预计程序仍然无法编译。

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

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

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

清单 7-3。看这里的操场。

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

❌ 清单 7-4

嗯,让我们分配一个变量给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);
}

清单 7-4。看这里的操场。

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

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

我们做什么?

✅ 清单 7–5

names我们可以从using中借用元素.iter()

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

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

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

清单 7-5。看这里的操场。

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

✅ 清单 7–6

我们可以使用切片(实现IntoIterator)。

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

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

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

清单 7-6。看这里的操场。

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

✅ 清单 7–7

如果你的程序允许,我们可以稍微切换一下代码以先借用names,然后移动它:

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

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

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

清单 7-7。看这里的操场。

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

✅ 清单 7–8

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

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

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

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

清单 7-8。看这里的操场。

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

函数式编程

使用函数式编程习语可以更明显地表明,当您想要迭代元素时(特别是如果您来自不必直接处理迭代器的编程语言)涉及迭代器。

与 for 循环不同,没有隐式.into_iter()调用。因此,对于向量,我们总是需要调用.iter(),.into_iter()等来获取迭代器。

❌ 清单 7–9

在这里它不会编译,因为我们需要遍历元素,这意味着我们需要使用.iter().

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

清单 7-9。在这里看游乐场

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

✅ 清单 7–10

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

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

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

清单 7-10。在这里看游乐场

.John
Jane
["John", "Jane"]

❌ 清单 7–11

如果要移动元素,请使用.into_iter(). 但是,请注意,我们不能在之后使用向量:

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

清单 7-11。看这里的操场。

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

✅ 清单 7–12

您需要重新设计程序,以便不使用移动的元素。在这里,我们将 print 语句移到了前面:

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

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

清单 7-12。看这里的操场。

["John", "Jane"]
John
Jane

✅ 清单 7–13

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

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

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

清单 7-13。看这里的操场。

John
Jane
["John", "Jane"]

 

8. &str

共享引用 ( &T) 也是Copy(参见此处)。这是一个常用类型的示例,字符串 slice &str

✅ 清单 8–1:

我们可以在第 3 行和第 4 行中传递nameto do_something。请注意,我们正在复制引用。

fn main() {
    let name: &'static str = "Rust";
    do_something(name);
    do_something(name);
}

fn do_something(name: &str) {
    println!("Hello, {:?}!", name);
}

清单 8-1。看这里的操场。

Hello, Rust!
Hello, Rust!

✅ ⚠️ 清单 8–2:

与清单 2-4 类似,在传递to.clone()之前调用是多余的。namedo_something

fn main() {
    let name: &'static str = "Rust";
    do_something(name.clone());
    do_something(name.clone());
}

fn do_something(name: &str) {
    println!("Hello, {:?}!", name);
}

清单 8-2。看这里的操场。

Hello, Rust!
Hello, Rust!

✅ ⚠️ 清单 8-3:

由 Clippy 建议,该<&str>::clone(&name)成语克隆了引用。该程序可以编译,但我不确定这是否是惯用的。

fn main() {
    let name: &'static str = "Rust";
    do_something(<&str>::clone(&name));
    do_something(<&str>::clone(&name));
}

fn do_something(name: &str) {
    println!("Hello, {:?}!", name);
}

清单 8-3。看这里的操场。

Hello, Rust!
Hello, Rust!

 

就是这样,伙计们!

来源:https ://itnext.io/rust-ownership-50-code-examples-96203fcf79ea

#rust 

What is GEEK

Buddha Community

Rust 所有权:50 个代码示例

Serde Rust: Serialization Framework for Rust

Serde

*Serde is a framework for serializing and deserializing Rust data structures efficiently and generically.*

You may be looking for:

Serde in action

Click to show Cargo.toml. Run this code in the playground.

[dependencies]

# The core APIs, including the Serialize and Deserialize traits. Always
# required when using Serde. The "derive" feature is only required when
# using #[derive(Serialize, Deserialize)] to make Serde work with structs
# and enums defined in your crate.
serde = { version = "1.0", features = ["derive"] }

# Each data format lives in its own crate; the sample code below uses JSON
# but you may be using a different one.
serde_json = "1.0"

 

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let point = Point { x: 1, y: 2 };

    // Convert the Point to a JSON string.
    let serialized = serde_json::to_string(&point).unwrap();

    // Prints serialized = {"x":1,"y":2}
    println!("serialized = {}", serialized);

    // Convert the JSON string back to a Point.
    let deserialized: Point = serde_json::from_str(&serialized).unwrap();

    // Prints deserialized = Point { x: 1, y: 2 }
    println!("deserialized = {:?}", deserialized);
}

Getting help

Serde is one of the most widely used Rust libraries so any place that Rustaceans congregate will be able to help you out. For chat, consider trying the #rust-questions or #rust-beginners channels of the unofficial community Discord (invite: https://discord.gg/rust-lang-community), the #rust-usage or #beginners channels of the official Rust Project Discord (invite: https://discord.gg/rust-lang), or the #general stream in Zulip. For asynchronous, consider the [rust] tag on StackOverflow, the /r/rust subreddit which has a pinned weekly easy questions post, or the Rust Discourse forum. It's acceptable to file a support issue in this repo but they tend not to get as many eyes as any of the above and may get closed without a response after some time.

Download Details:
Author: serde-rs
Source Code: https://github.com/serde-rs/serde
License: View license

#rust  #rustlang 

Awesome  Rust

Awesome Rust

1654894080

Serde JSON: JSON Support for Serde Framework

Serde JSON

Serde is a framework for serializing and deserializing Rust data structures efficiently and generically.

[dependencies]
serde_json = "1.0"

You may be looking for:

JSON is a ubiquitous open-standard format that uses human-readable text to transmit data objects consisting of key-value pairs.

{
    "name": "John Doe",
    "age": 43,
    "address": {
        "street": "10 Downing Street",
        "city": "London"
    },
    "phones": [
        "+44 1234567",
        "+44 2345678"
    ]
}

There are three common ways that you might find yourself needing to work with JSON data in Rust.

  • As text data. An unprocessed string of JSON data that you receive on an HTTP endpoint, read from a file, or prepare to send to a remote server.
  • As an untyped or loosely typed representation. Maybe you want to check that some JSON data is valid before passing it on, but without knowing the structure of what it contains. Or you want to do very basic manipulations like insert a key in a particular spot.
  • As a strongly typed Rust data structure. When you expect all or most of your data to conform to a particular structure and want to get real work done without JSON's loosey-goosey nature tripping you up.

Serde JSON provides efficient, flexible, safe ways of converting data between each of these representations.

Operating on untyped JSON values

Any valid JSON data can be manipulated in the following recursive enum representation. This data structure is serde_json::Value.

enum Value {
    Null,
    Bool(bool),
    Number(Number),
    String(String),
    Array(Vec<Value>),
    Object(Map<String, Value>),
}

A string of JSON data can be parsed into a serde_json::Value by the serde_json::from_str function. There is also from_slice for parsing from a byte slice &[u8] and from_reader for parsing from any io::Read like a File or a TCP stream.

use serde_json::{Result, Value};

fn untyped_example() -> Result<()> {
    // Some JSON input data as a &str. Maybe this comes from the user.
    let data = r#"
        {
            "name": "John Doe",
            "age": 43,
            "phones": [
                "+44 1234567",
                "+44 2345678"
            ]
        }"#;

    // Parse the string of data into serde_json::Value.
    let v: Value = serde_json::from_str(data)?;

    // Access parts of the data by indexing with square brackets.
    println!("Please call {} at the number {}", v["name"], v["phones"][0]);

    Ok(())
}

The result of square bracket indexing like v["name"] is a borrow of the data at that index, so the type is &Value. A JSON map can be indexed with string keys, while a JSON array can be indexed with integer keys. If the type of the data is not right for the type with which it is being indexed, or if a map does not contain the key being indexed, or if the index into a vector is out of bounds, the returned element is Value::Null.

When a Value is printed, it is printed as a JSON string. So in the code above, the output looks like Please call "John Doe" at the number "+44 1234567". The quotation marks appear because v["name"] is a &Value containing a JSON string and its JSON representation is "John Doe". Printing as a plain string without quotation marks involves converting from a JSON string to a Rust string with as_str() or avoiding the use of Value as described in the following section.

The Value representation is sufficient for very basic tasks but can be tedious to work with for anything more significant. Error handling is verbose to implement correctly, for example imagine trying to detect the presence of unrecognized fields in the input data. The compiler is powerless to help you when you make a mistake, for example imagine typoing v["name"] as v["nmae"] in one of the dozens of places it is used in your code.

Parsing JSON as strongly typed data structures

Serde provides a powerful way of mapping JSON data into Rust data structures largely automatically.

use serde::{Deserialize, Serialize};
use serde_json::Result;

#[derive(Serialize, Deserialize)]
struct Person {
    name: String,
    age: u8,
    phones: Vec<String>,
}

fn typed_example() -> Result<()> {
    // Some JSON input data as a &str. Maybe this comes from the user.
    let data = r#"
        {
            "name": "John Doe",
            "age": 43,
            "phones": [
                "+44 1234567",
                "+44 2345678"
            ]
        }"#;

    // Parse the string of data into a Person object. This is exactly the
    // same function as the one that produced serde_json::Value above, but
    // now we are asking it for a Person as output.
    let p: Person = serde_json::from_str(data)?;

    // Do things just like with any other Rust data structure.
    println!("Please call {} at the number {}", p.name, p.phones[0]);

    Ok(())
}

This is the same serde_json::from_str function as before, but this time we assign the return value to a variable of type Person so Serde will automatically interpret the input data as a Person and produce informative error messages if the layout does not conform to what a Person is expected to look like.

Any type that implements Serde's Deserialize trait can be deserialized this way. This includes built-in Rust standard library types like Vec<T> and HashMap<K, V>, as well as any structs or enums annotated with #[derive(Deserialize)].

Once we have p of type Person, our IDE and the Rust compiler can help us use it correctly like they do for any other Rust code. The IDE can autocomplete field names to prevent typos, which was impossible in the serde_json::Value representation. And the Rust compiler can check that when we write p.phones[0], then p.phones is guaranteed to be a Vec<String> so indexing into it makes sense and produces a String.

The necessary setup for using Serde's derive macros is explained on the Using derive page of the Serde site.

Constructing JSON values

Serde JSON provides a json! macro to build serde_json::Value objects with very natural JSON syntax.

use serde_json::json;

fn main() {
    // The type of `john` is `serde_json::Value`
    let john = json!({
        "name": "John Doe",
        "age": 43,
        "phones": [
            "+44 1234567",
            "+44 2345678"
        ]
    });

    println!("first phone number: {}", john["phones"][0]);

    // Convert to a string of JSON and print it out
    println!("{}", john.to_string());
}

The Value::to_string() function converts a serde_json::Value into a String of JSON text.

One neat thing about the json! macro is that variables and expressions can be interpolated directly into the JSON value as you are building it. Serde will check at compile time that the value you are interpolating is able to be represented as JSON.

let full_name = "John Doe";
let age_last_year = 42;

// The type of `john` is `serde_json::Value`
let john = json!({
    "name": full_name,
    "age": age_last_year + 1,
    "phones": [
        format!("+44 {}", random_phone())
    ]
});

This is amazingly convenient, but we have the problem we had before with Value: the IDE and Rust compiler cannot help us if we get it wrong. Serde JSON provides a better way of serializing strongly-typed data structures into JSON text.

Creating JSON by serializing data structures

A data structure can be converted to a JSON string by serde_json::to_string. There is also serde_json::to_vec which serializes to a Vec<u8> and serde_json::to_writer which serializes to any io::Write such as a File or a TCP stream.

use serde::{Deserialize, Serialize};
use serde_json::Result;

#[derive(Serialize, Deserialize)]
struct Address {
    street: String,
    city: String,
}

fn print_an_address() -> Result<()> {
    // Some data structure.
    let address = Address {
        street: "10 Downing Street".to_owned(),
        city: "London".to_owned(),
    };

    // Serialize it to a JSON string.
    let j = serde_json::to_string(&address)?;

    // Print, write to a file, or send to an HTTP server.
    println!("{}", j);

    Ok(())
}

Any type that implements Serde's Serialize trait can be serialized this way. This includes built-in Rust standard library types like Vec<T> and HashMap<K, V>, as well as any structs or enums annotated with #[derive(Serialize)].

Performance

It is fast. You should expect in the ballpark of 500 to 1000 megabytes per second deserialization and 600 to 900 megabytes per second serialization, depending on the characteristics of your data. This is competitive with the fastest C and C++ JSON libraries or even 30% faster for many use cases. Benchmarks live in the serde-rs/json-benchmark repo.

Getting help

Serde is one of the most widely used Rust libraries, so any place that Rustaceans congregate will be able to help you out. For chat, consider trying the #rust-questions or #rust-beginners channels of the unofficial community Discord (invite: https://discord.gg/rust-lang-community), the #rust-usage or #beginners channels of the official Rust Project Discord (invite: https://discord.gg/rust-lang), or the #general stream in Zulip. For asynchronous, consider the [rust] tag on StackOverflow, the /r/rust subreddit which has a pinned weekly easy questions post, or the Rust Discourse forum. It's acceptable to file a support issue in this repo, but they tend not to get as many eyes as any of the above and may get closed without a response after some time.

No-std support

As long as there is a memory allocator, it is possible to use serde_json without the rest of the Rust standard library. This is supported on Rust 1.36+. Disable the default "std" feature and enable the "alloc" feature:

[dependencies]
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }

For JSON support in Serde without a memory allocator, please see the serde-json-core crate.

Link: https://crates.io/crates/serde_json

#rust  #rustlang  #encode   #json 

Rust Lang Course For Beginner In 2021: Guessing Game

 What we learn in this chapter:
- Rust number types and their default
- First exposure to #Rust modules and the std::io module to read input from the terminal
- Rust Variable Shadowing
- Rust Loop keyword
- Rust if/else
- First exposure to #Rust match keyword

=== Content:
00:00 - Intro & Setup
02:11 - The Plan
03:04 - Variable Secret
04:03 - Number Types
05:45 - Mutability recap
06:22 - Ask the user
07:45 - First intro to module std::io
08:29 - Rust naming conventions
09:22 - Read user input io:stdin().read_line(&mut guess)
12:46 - Break & Understand
14:20 - Parse string to number
17:10 - Variable Shadowing
18:46 - If / Else - You Win, You Loose
19:28 - Loop
20:38 - Match
23:19 - Random with rand
26:35 - Run it all
27:09 - Conclusion and next episode

#rust 

黎 飞

黎 飞

1661542200

Rust 所有权:50 个代码示例

遇到所有权问题,例如借用已经转移的价值?如果你是,那么我有 50 个代码片段(好吧,有 53 个)供我们在 Rust Playground 中一起练习。

本文假设您大致了解基本类型,如、 、、迭代器、切片和结构等String整数类型。我假设你已经阅读了Rust 编程语言(尤其是第 4 章:理解所有权)和Rust by Example书籍。i32Vec

这些示例以简单类型(以 开头String)开始,然后移至稍微更“复杂”的类型。✅ 表示代码可以编译,❌ 表示不能。虽然一些示例建议了无法编译的代码的替代方案,但请注意,这些选项并不详尽,仅适用于初学者。这些示例不包括可变性(因为我觉得它们更容易掌握)并且不处理所有权异步编程。

1.字符串

AString是一种不实现Copy特征的类型。

✅ 清单 1–1

让我们用它创建一个Stringthen 调用do_something

一切看起来都很正常……

fn main() {
    let name = String::from("Rust");
    do_something(name);
}

fn do_something(name: String) {
    println!("Hello, {}!", name);
}

清单 1-1。在这里看游乐场

.Hello, Rust!

❌ 清单 1-2

让我们在第 4 行添加一个println!语句……

fn main() {
    let name = String::from("Rust");
    do_something(name);
    println!("{}", name);
}

fn do_something(name: String) {
    println!("Hello, {}!", name);
}

清单 1-2。在这里看游乐场

.error[E0382]: borrow of moved value: `name`
 --> src/main.rs:4:20

这是非法的!这是因为我们试图使用(借用,在 的情况下println!name已经do_something在第 3 行移入的值。

您不能使用已移动的值。在namecan reach之前,它在函数结束时println!被移入do_something超出范围。

我们能做什么?

✅ 清单 1-3

我们可以做的是将 name 克隆为name_clone,这样我们将 name 用于第 5 行和name_clone第 6 行。请注意,克隆是有成本的。

fn main() {
    let name = String::from("Rust");
    let name_clone = name.clone();

    do_something(name);
    println!("{}", name_clone);
}

fn do_something(name: String) {
    println!("Name: {}!", name);
}

清单 1-3。看这里的操场。

Name: Rust!
Rust

✅ 清单 1-4

或者我们可以在第 3 行中通过引用传递namedo_something,并且仍然可以println!在第 4 行中使用它。

fn main() {
    let name = String::from("Rust");
    do_something(&name);
    println!("{}", name);
}

fn do_something(name: &str) {
    println!("Hello, {}!", name);
}

清单 1-4。看这里的操场。

Hello, Rust!
Rust

✅ 清单 1-5

或者,如果您的程序允许,我们可以简单地切换一下代码。

在这里,我们首先借用name,然后将其移入do_something。该程序可以编译,因为在该do_something行之后,main范围内没有其他人将使用name.

fn main() {
    let name = String::from("Rust");
    println!("{}", name);
    do_something(name);
}

fn do_something(name: String) {
    println!("Hello, {}!", name);
}

清单 1-5。看这里的操场。

Rust
Hello, Rust!

 

2. i32

Copy对于实现trait的类型来说,生活要容易得多,比如i32.

✅ 清单 2–1

再次,让我们从简单的事情开始。我们创建age并调用do_something它。在这里,age复制了 的值,因为该i32类型实现了Copytrait。

fn main() {
    let age: i32 = 25;
    do_something(age);
}

fn do_something(age: i32) {
    println!("Hello, {}!", age);
}

清单 2-1。看这里的操场。

Hello, 25!

✅ 清单 2–2

的值age在第 4 行再次复制。

fn main() {
    let age: i32 = 25;
    do_something(age);
	println!("{}", age);
}

fn do_something(age: i32) {
    println!("Hello, {}!", age);
}

清单 2-2。看这里的操场。

Hello, 25!
25

✅ 清单 2–3

我们也可以通过引用传递值,尽管我认为这不是惯用的。

fn main() {
    let age: i32 = 25;
    do_something(&age);
    println!("{}", age);
}

fn do_something(age: &i32) {
    println!("Hello, {}!", age);
}

清单 2-3。看这里的操场。

Hello, 25!
25

✅ ⚠️ 清单 2-4

在第 3 行中,我们首先进行了克隆age,然后将其传递给do_something. 程序会编译,但请注意复制将进行两次——一次在 中,另一次是在进入作用域.clone()时进行复制。agedo_something

请注意,应避免使用此成语。请参阅此处的clone_on_copy 上的 Clippy lint 。

fn main() {
    let age: i32 = 25;
    do_something(age.clone());
    println!("{}", age);
}

fn do_something(age: i32) {
    println!("Hello, {}!", age);
}

清单 2-4。看这里的操场。

Hello, 25!
25

 

3. 带String字段的结构

如果一个类型Copy的所有组件都实现了,那么它就可以实现Copy(请参阅我的类型何时可以是Copy)。

在这些清单中,我们关注的是由一个实现的字段组成的Movie结构。结果,无法实施。StringCopyMovie Copy

✅ 清单 3–1

到目前为止看起来不错,不是吗?程序编译(忽略未使用的字段警告),我们都很高兴。

#[derive(Debug)]
struct Movie {
    title: String,
}
fn main() {
    let movie = Movie { 
        title: String::from("Rust")
    };
    do_something(movie);
}
fn do_something(movie: Movie) {
    println!("Movie: {:?}!", movie);
}

清单 3-1。看这里的操场。

Movie: Movie { title: "Rust" }!

❌ 清单 3-2

但是在添加之后println!,编译器抱怨我们试图借用一个movie已经移入的值do_something

String我们在前面的清单中已经看到过这种移动语义 for 's 。

#[derive(Debug)]
struct Movie {
    title: String,
}
fn main() {
    let movie = Movie { 
        title: String::from("Rust")
    };
    do_something(movie);
    println!("Movie: {:?}", movie);
}
fn do_something(movie: Movie) {
    println!("Movie: {:?}!", movie);
}

清单 3-2。看这里的操场。

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

我们做什么?

✅ 清单 3–3

我们可以借用movie而不是移动它。

#[derive(Debug)]
struct Movie {
    title: String,
}
fn main() {
    let movie = Movie { 
        title: String::from("Rust")
    };
    do_something(&movie);
    println!("Movie: {:?}", movie);
}
fn do_something(movie: &Movie) {
    println!("Movie: {:?}!", movie);
}

清单 3-3。看这里的操场。

Movie: Movie { title: "Rust" }!
Movie: Movie { title: "Rust" }

✅ 清单 3–4

或者我们可以克隆moviedo_something. 这需要Movie实施Clone

#[derive(Debug, Clone)]
struct Movie {
    title: String,
}
fn main() {
    let movie = Movie { 
        title: String::from("Rust")
    };
    do_something(movie.clone());
    println!("Movie: {:?}", movie);
}
fn do_something(movie: Movie) {
    println!("Movie: {:?}!", movie);
}

清单 3-4。看这里的操场。

Movie: Movie { title: "Rust" }!
Movie: Movie { title: "Rust" }

✅ 清单 3–5

或者,如果您的程序允许,我们可以在不克隆的情况下稍微切换代码。在这里,借来movieprintln!然后movie搬进do_something

#[derive(Debug)]
struct Movie {
    title: String,
}
fn main() {
    let movie = Movie { 
        title: String::from("Rust")
    };
    println!("Movie: {:?}", movie);
    do_something(movie);
}
fn do_something(movie: Movie) {
    println!("Movie: {:?}!", movie);
}

清单 3-5。看这里的操场。

Movie: Movie { title: "Rust" }
Movie: Movie { title: "Rust" }!

 

4.带有u8字段的结构体

如前所述,Copy如果一个类型的所有组件都实现了,则该类型可以实现Copy

在这些清单中,我们关注的是由一个实现特征的字段组成的Book结构。结果,可以实施。u8CopyMovie Copy

✅ 清单 4–1

在这个清单中,一切看起来都很正常,对吧?

#[derive(Debug)]
struct Book {
    id: u8,
}
fn main() {
    let book = Book { id: 1 };
    do_something(book);
}
fn do_something(book: Book) {
    println!("Book: {:?}!", book);
}

清单 4-1。看这里的操场。

Book: Book { id: 1 }!

❌ 清单 4-2

直到我们在println!下面添加声明do_something。发生了什么?

因为book没有实现Copytrait(或者还没有实现),Rust进入 . 但是……因为被移动了,不能再使用这个值了。bookdo_somethingbookprintln!

#[derive(Debug)]
struct Book {
    id: u8,
}
fn main() {
    let book = Book { id: 1 };
    do_something(book);
    println!("Book: {:?}", book);
}
fn do_something(book: Book) {
    println!("Book: {:?}!", book);
}

清单 4-2。看这里的操场。

error[E0382]: borrow of moved value: `book`
 --> src/main.rs:8:28

我们能做什么?

❌ 清单 4-3

一种方法是Copy通过派生来实现 Book 的特征Copy

#[derive(Debug, Copy)]
struct Book {
    id: u8,
}
fn main() {
    let book = Book { id: 1 };
    do_something(book);
    println!("Book: {:?}", book);
}
fn do_something(book: Book) {
    println!("Book: {:?}!", book);
}

清单 4-3。看这里的操场。

error[E0277]: the trait bound `Book: Clone` is not satisfied
 --> src/main.rs:1:17

但是……我们仍然得到一个错误——特征绑定Book: Clone不满足。

✅ 清单 4–4

如果我们转到docs,它说所有必须实现的东西也Copy必须实现Clone,因为Clone它是一个超特征。

因此,让我们在现有特征之上实现Clone特征。BookCopy

#[derive(Debug, Copy, Clone)]
struct Book {
    id: u8,
}
fn main() {
    let book = Book { id: 1 };
    do_something(book);
    println!("Book: {:?}", book);
}
fn do_something(book: Book) {
    println!("Book: {:?}!", book);
}

清单 4-4。看这里的操场。

Book: Book { id: 1 }!
Book: Book { id: 1 }

✅ 清单 4–5

甜的!我们还可以重新设计我们的程序,以便改为do_something借用。book

#[derive(Debug)]
struct Book {
    id: u8,
}
fn main() {
    let book = Book { id: 1 };
    do_something(&book);
    println!("Book: {:?}", book);
}
fn do_something(book: &Book) {
    println!("Book: {:?}!", book);
}

清单 4-5。看这里的操场。

Book: Book { id: 1 }!
Book: Book { id: 1 }

 

5. 带有两个字符串字段的结构

可以将值移出结构吗?🤔

在本节中,我们将讨论部分移动

✅ 清单 5–1

到目前为止一切看起来都很正常……

#[derive(Debug)]
struct Person {
    name: String,
    alias: String,
}
fn main() {
    let person = Person { 
        name: "John".to_string(),
        alias: "Johan".to_string(),
    };
    print_alias(person.alias);
}
fn print_alias(alias: String) {
    println!("Person: {:?}!", alias);
}

清单 5-1。看这里的操场。

Person: "Johan"!

❌ 清单 5-2

然而,在这个清单中,我们在print_alias.

该程序没有编译,因为person.alias已经部分移入print_alias但我们尝试在该行person中整体借用。println!

#[derive(Debug)]
struct Person {
    name: String,
    alias: String,
}
fn main() {
    let person = Person { 
        name: "John".to_string(),
        alias: "Johan".to_string(),
    };
    print_alias(person.alias);
    println!("{:?}", person);
}
fn print_alias(alias: String) {
    println!("Person: {:?}!", alias);
}

清单 5-2。看这里的操场。

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

我们能做什么?有哪些合法举措?

事实证明,我们可以部分移动字段——只要确保我们以后不再尝试使用结构本身。

在这里,我们将这两个字段移动到两个不同的函数中。

#[derive(Debug)]
struct Person {
    name: String,
    alias: String,
}

fn main() {
    let person = Person { 
        name: "John".to_string(),
        alias: "Johan".to_string(),
    };
    print_alias(person.alias);
    print_name(person.name);
}

fn print_alias(alias: String) {
    println!("Alias: {:?}!", alias);
}

fn print_name(name: String) {
    println!("Name: {:?}!", name);
}

清单 5-3。看这里的操场。

Alias: "Johan"!
Name: "John"!

✅ 清单 5–4

我们可以通过引用传递这些字段。

#[derive(Debug)]
struct Person {
    name: String,
    alias: String,
}

fn main() {
    let person = Person { 
        name: "John".to_string(),
        alias: "Johan".to_string(),
    };
    print_alias(&person.alias);
    print_name(&person.name);
}

fn print_alias(alias: &str) {
    println!("Alias: {:?}!", alias);
}

fn print_name(name: &str) {
    println!("Name: {:?}!", name);
}

清单 5-4。看这里的操场。

Alias: "Johan"!
Name: "John"!

✅ 清单 5–5

或者如果我们以后想person再次使用,我们可以克隆这些值。

#[derive(Debug)]
struct Person {
    name: String,
    alias: String,
}

fn main() {
    let person = Person { 
        name: "John".to_string(),
        alias: "Johan".to_string(),
    };
    print_alias(person.alias.clone());
    print_name(person.name.clone());
    println!("{:?}", person);
}

fn print_alias(alias: String) {
    println!("Alias: {:?}!", alias);
}

fn print_name(name: String) {
    println!("Name: {:?}!", name);
}

清单 5-5。看这里的操场。

Alias: "Johan"!
Name: "John"!
Person { name: "John", alias: "Johan" }

✅ 清单 5–6

或者你可以移动东西而不必克隆。在这里,我们首先借用了person,然后部分移动了这些字段。

#[derive(Debug)]
struct Person {
    name: String,
    alias: String,
}

fn main() {
    let person = Person { 
        name: "John".to_string(),
        alias: "Johan".to_string(),
    };
    println!("{:?}", person);
    print_alias(person.alias);
    print_name(person.name);
}

fn print_alias(alias: String) {
    println!("Alias: {:?}!", alias);
}

fn print_name(name: String) {
    println!("Name: {:?}!", name);
}

清单 5-6。看这里的操场。

Person { name: "John", alias: "Johan" }
Alias: "Johan"!
Name: "John"!

 

6. Vec<字符串>

String, Vec's 被移动了,因为它们没有实现Copy特征(见这里)。相同的移动语义适用。

向量(以及与此相关的其他集合)值得讨论,因为它涉及到很多语义——容器本身、元素和迭代器。

我们Vec在这里用作示例,因为它是集合中非常常见的数据结构。其他也没有实现该Copy特征的集合包括HashMapHashSet。另一方面,数组的移动语义与结构类似,因为它们取决于项目的类型——但那是另一天的事了。

✅ 清单 6–1

与往常一样,我们将从快乐的事情开始:

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

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

清单 6-1。看这里的操场。

["John", "Jane"]

❌ 清单 6–2

直到我们println!do_something.

String对于我们在移动后尝试借用/使用值的类型,我们之前已经看到过这个编译器错误。

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

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

清单 6-2。看这里的操场。

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

我们能做什么?

✅ 清单 6–3

我们可以先借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);
}

清单 6-3。看这里的操场。

["John", "Jane"]
["John", "Jane"]

✅ 清单 6–4

我们可以重新设计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);
}

清单 6-4。看这里的操场。

["John", "Jane"]
["John", "Jane"]

✅ 清单 6–5

最后,我们可以克隆 的向量do_something。请注意,向量克隆是浅拷贝。

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

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

清单 6-5。看这里的操场。

["John", "Jane"]
["John", "Jane"]

通过获取索引来读取向量中的元素是很棘手的。只是在我们所知道的编程语言中不一样。

❌ 清单 6–6

我的意思是你会看看这个!下面的代码在我知道的编程语言中非常好,但是在这里,我们得到一个编译器错误,说我不能移出Vec<String>.

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

清单 6-6。在这里看游乐场

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

但为什么?

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

Vec不允许隐式移出 a ,因为它会使向量处于无效状态——一个元素被移出,其他元素则不被移出(请参阅StackOverflow 帖子)。如果我要迭代这个向量,我可能会访问一个无效的内存(被移出的元素)😱。谢谢你保护我们,Rust。

那么我们该怎么办?

✅ 清单 6–7

我们可以使用索引运算符借用我们想要的值。

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

清单 6-7。看这里的操场。

Hello, John

✅ 清单 6–8

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

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

清单 6-8。看这里的操场。

Hello, John

✅ 清单 6–9

.first()我们可以使用or方法借用该值.last()(当然,如果您想要第一项和最后一项)。

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

清单 6-9。看这里的操场。

Hello, John

但是……如果我想拥有一个元素怎么办?

✅ 清单 6–10

我们使用该.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);
}

清单 6-10。看这里的操场。

Hello, John

如果您想拥有第一个元素,则此示例有效。Afaik,如果你想拥有索引n处的元素(出于某种原因),你会调用.skip(n)then call .next()

✅ 清单 6–11

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

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

清单 6-11。看这里的操场。

Hello, Jane

 

7. 迭代器

当涉及到集合中元素的所有权时,迭代器扮演着极其重要的角色。

在这些示例中,我们将使用Vec<String>,有意地String用作元素(它不实现Copy特征),以便我们可以在向量中展示其移动语义。

for循环

✅ 清单 7–1

让我们从一个 for 循环迭代开始names。为什么是for循环?我们会解决的。

但就目前而言,生活是美轮美奂的:

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

清单 7-1。看这里的操场。

John
Jane

❌ 清单 7–2

直到我们在 for 循环下面添加一个 print 语句……编译器开始抱怨。

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

清单 7-2。看这里的操场。

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

显然有一个“.into_iter循环前的隐式调用”。

等等,隐含的?为什么?!但是好的,让我们添加隐含的.into_iter

❌ 清单 7-3

添加后into_iter(),我们预计程序仍然无法编译。

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

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

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

清单 7-3。看这里的操场。

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

❌ 清单 7-4

嗯,让我们分配一个变量给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);
}

清单 7-4。看这里的操场。

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

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

我们做什么?

✅ 清单 7–5

names我们可以从using中借用元素.iter()

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

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

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

清单 7-5。看这里的操场。

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

✅ 清单 7–6

我们可以使用切片(实现IntoIterator)。

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

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

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

清单 7-6。看这里的操场。

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

✅ 清单 7–7

如果你的程序允许,我们可以稍微切换一下代码以先借用names,然后移动它:

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

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

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

清单 7-7。看这里的操场。

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

✅ 清单 7–8

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

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

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

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

清单 7-8。看这里的操场。

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

函数式编程

使用函数式编程习语可以更明显地表明,当您想要迭代元素时(特别是如果您来自不必直接处理迭代器的编程语言)涉及迭代器。

与 for 循环不同,没有隐式.into_iter()调用。因此,对于向量,我们总是需要调用.iter(),.into_iter()等来获取迭代器。

❌ 清单 7–9

在这里它不会编译,因为我们需要遍历元素,这意味着我们需要使用.iter().

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

清单 7-9。在这里看游乐场

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

✅ 清单 7–10

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

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

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

清单 7-10。在这里看游乐场

.John
Jane
["John", "Jane"]

❌ 清单 7–11

如果要移动元素,请使用.into_iter(). 但是,请注意,我们不能在之后使用向量:

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

清单 7-11。看这里的操场。

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

✅ 清单 7–12

您需要重新设计程序,以便不使用移动的元素。在这里,我们将 print 语句移到了前面:

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

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

清单 7-12。看这里的操场。

["John", "Jane"]
John
Jane

✅ 清单 7–13

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

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

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

清单 7-13。看这里的操场。

John
Jane
["John", "Jane"]

 

8. &str

共享引用 ( &T) 也是Copy(参见此处)。这是一个常用类型的示例,字符串 slice &str

✅ 清单 8–1:

我们可以在第 3 行和第 4 行中传递nameto do_something。请注意,我们正在复制引用。

fn main() {
    let name: &'static str = "Rust";
    do_something(name);
    do_something(name);
}

fn do_something(name: &str) {
    println!("Hello, {:?}!", name);
}

清单 8-1。看这里的操场。

Hello, Rust!
Hello, Rust!

✅ ⚠️ 清单 8–2:

与清单 2-4 类似,在传递to.clone()之前调用是多余的。namedo_something

fn main() {
    let name: &'static str = "Rust";
    do_something(name.clone());
    do_something(name.clone());
}

fn do_something(name: &str) {
    println!("Hello, {:?}!", name);
}

清单 8-2。看这里的操场。

Hello, Rust!
Hello, Rust!

✅ ⚠️ 清单 8-3:

由 Clippy 建议,该<&str>::clone(&name)成语克隆了引用。该程序可以编译,但我不确定这是否是惯用的。

fn main() {
    let name: &'static str = "Rust";
    do_something(<&str>::clone(&name));
    do_something(<&str>::clone(&name));
}

fn do_something(name: &str) {
    println!("Hello, {:?}!", name);
}

清单 8-3。看这里的操场。

Hello, Rust!
Hello, Rust!

 

就是这样,伙计们!

来源:https ://itnext.io/rust-ownership-50-code-examples-96203fcf79ea

#rust 

Lydia  Kessler

Lydia Kessler

1626318000

ULTIMATE Rust Lang Tutorial! - Publishing a Rust Crate

The ultimate Rust lang tutorial. Follow along as we go through the Rust lang book chapter by chapter.

📝Get the FREE Rust Cheatsheet: https://letsgetrusty.com/cheatsheet

The Rust book: https://doc.rust-lang.org/stable/book/​​

Chapters:
0:00​ Intro
0:43 Release Profiles
3:00 Documentation Comments
4:32 Commonly Used Sections
5:04 Documentation Comments as Tests
5:50 Commenting Contained Items
6:29 Exporting a Public API
8:44 Setting up Creates.io Account
9:54 Adding Metadata to a New Create
12:14 Publishing to Crates.io
12:49 Removing Version from Crates.io
13:37 Outro

#letsgetrusty​​ #rust​lang​ #tutorial

#rust #rust lang #rust crate