我对Rust
非常陌生,来自python
编程语言,我构建了以下逻辑:
main.rs
use library_system::Library;
use library_system::Book;
fn main() {
let library = Library {
name: String::from("Malcom X")
};
let book = Book {
name: String::from("The Edge of AI"),
author: String::from("Clement Adams"),
released_date: String::from("06/01/2021"),
library,
};
println!("library created: {:?}", library);
println!("booked created {:?}", book);
}
lib.rs
#[derive(Debug)]
pub enum Status {
Free,
Booked,
}
#[derive(Debug)]
pub struct Library {
pub name: String,
}
#[derive(Debug)]
pub struct Book {
pub name: String,
pub author: String,
pub released_date: String,
pub library: Library,
}
由于某种原因,当我试图在终端中打印上面代码的结果时,我在编译时收到了这样的消息:
error[E0382]: borrow of moved value: `library`
--> src/main.rs:19:39
|
6 | let library = Library {
| ------- move occurs because `library` has type `Library`, which does not implement the `Copy` trait
...
16 | library,
| ------- value moved here
...
19 | println!("library created: {:?}", library);
| ^^^^^^^ value borrowed here after move
解决这个问题的适当方法是什么?
我试着解决这个问题:
let library_clone = library.clone();
println!("library created: {:?}", library_clone);
但我现在面对的问题是:
error[E0599]: no method named `clone` found for struct `Library` in the current scope
--> src/main.rs:10:33
|
10 | let library_clone = library.clone();
| ^^^^^ method not found in `Library`
2条答案
按热度按时间hujrc8aj1#
Rust采用单一所有权模型,这意味着每一个值在代码中的一个位置上都是唯一的。默认情况下,当你传递一个参数给一个函数时,你传递它 * 通过值 *,这意味着你传递完全的所有权给这个函数。一个必然的结果是,如果你将所有权传递给一个函数,你就不再有权对这个值做任何事情。(实现
Copy
trait的类型,例如数值类型,是例外,但您的类型不符合Copy
)如果你想让一个函数查看你的数据,而不给它完全的所有权,你应该传递一个对函数的引用。传递一个引用允许函数借用你的值而不完全控制它。
注意
library
之前的&
。此外,由于您提到了
clone
,您可能应该为您的结构实现它。你可以用手来做但是“通过克隆
self
的所有部分来克隆self
”的模式是如此普遍,以至于Clone
trait有一个derive
宏。所以你可以只写h5qlskok2#
在Rust中,赋值的工作方式与Python或C++不同。对于Rust中的许多类型,当您将
var1
分配给var2
时,var1
中的值被移动到var2
中,然后var1
未初始化,不再包含该值。此后,编译器将不允许您使用未初始化的变量。如果您这样做:
你会得到这个错误:
根据The Rust Programming Language,该错误的原因是因为
s1
是一个指针,指向存储String的内存中的一个点,当您写入时:Rust将指针复制到s2中。这使得你有两个指针指向内存中的同一位置,当
s1
和s2
最终超出范围时,Rust将释放s1
和s2
指向的内存,这将导致相同的内存被释放两次,这被称为“双重释放”错误。为了防止相同的内存被释放两次,Rust在将
s1
指针分配给s2
后立即使其无效。这就是所谓的“一步棋”:指针已经从一个变量移动到另一个变量。***借用***是指创建对另一个变量的引用,即指向另一个变量的指针,即不是变量中值的副本,例如:
&library
,这通常不是错误。您的代码抛出了与上面的String示例相同的错误,因此看起来类似的事情正在发生。然而,我搜索了一下,被赋值为Struct的变量并不包含指向内存中某个位置的指针,所以不存在“双自由”问题。然而,下面的代码抛出了相同的“borrow after move”错误:
错误:
错误是说分配给s1变量的值已经移动到s2变量中,此后
println!()
试图使用s1值(但它不再存在了!).据我所知,规则似乎是:如果一个值没有实现Copy trait,那么当你将一个包含该值的变量赋值给另一个变量时,Rust会移动该值并使第一个变量无效。
在做了更多的研究之后....“Programming Rust(2nd Edition)”第85页,简单地说:
在Rust中,对于大多数类型,像将值分配给变量,将其传递给函数或从函数返回这样的操作不会复制值:他们移动它。线人放弃。。值到目的地,[源]变为未初始化...
你可能会感到惊讶,Rust会改变这些基本操作的含义;当然,在历史上,分配是一件应该被很好地确定下来事情。然而,如果你仔细观察不同的语言是如何选择处理作业的,你会发现不同学校之间实际上有很大的差异。
这本书解释了赋值如何在Python中使用以下代码:
在赋值
t = s
和u = s
中,Python将s
中的指针复制到t
和u
中,并将列表的引用计数增加到3。赋值是非常有效的,因为列表不会被复制。另一方面,Python必须保持列表的引用计数,并且只有当引用计数下降到0时,即当s
、t
和u
都退出作用域时,内存是否被释放。引用计数和随之而来的垃圾收集会产生开销,即会减慢执行速度。这本书将Python的赋值与C进行了比较。以下是本书中使用的C示例:
这本书说C++的作业:
复制向量,内存中最终有三个向量,总共包含九个字符串。不需要引用计数和垃圾收集:当变量超出作用域时,包含其副本的内存将被释放。但是,复制值(尤其是大型复杂值)需要更多的时间和内存。
这本书解释了一个类似的Rust示例:
初始化:
将向量从
s
移动到t
。向量的元素保持在内存中的位置。没有需要调整的引用计数,当t
超出范围时,将释放向量占用的内存。而且,编译器现在认为s
未初始化。那么当我们到达初始化时会发生什么:
这会将未初始化的值
s
分配给u
。Rust谨慎地禁止使用未初始化的值,因此编译器会拒绝此代码,并返回以下错误:[你得到了类似的移动错误]
回到你的程序。您将
library
变量分配给book Struct
中的一个字段,因此Rust将library
变量中的值移动到book Struct
中的字段中,并立即取消初始化library
变量。此后,编译器将不允许您使用未初始化的library
变量:你会得到一个错误对不起,这个变量中曾经有一个值,但是你把这个值移到了另一个变量中,现在这个变量没有初始化。然后,您随后尝试使用此变量。别这样!
你认为这段代码会发生什么:
这对于任何来自Python或C++的人来说都应该是非常令人惊讶的!
现在,可以更好地理解***借款。看看这个例子:
输出:
当你创建一个变量的引用时,例如
&s
,并将引用分配给另一个变量,则不会移动任何内容。此外,当t
和u
超出作用域时,不会释放包含向量的内存:输出:
只有当
s
超出作用域时,包含向量的内存才会被释放。