英文:
Cannot move a value of dyn type
问题
以下是代码的翻译部分:
我在处理这个错误时遇到了很大的困难,这是我的代码:
struct Dog {}
struct Cat {}
trait Animal {
fn make_sound(self) -> String;
}
impl Animal for Dog {
fn make_sound(self) -> String {
String::from("woof")
}
}
impl Animal for Cat {
fn make_sound(self) -> String {
String::from("meow")
}
}
fn choose(vec: Vec<Box<dyn Animal>>, boo: bool) -> String {
if boo {
vec[0].make_sound()
}
else {
vec[1].make_sound()
}
}
这是编译器的报错信息:
错误[E0161]: 无法移动类型为 `dyn Animal` 的值
--> src/main.rs:22:9
|
22 | vec[0].make_sound()
| ^^^^^^^^^^^^^^^^^^^ `dyn Animal` 的大小无法在编译时确定
错误[E0161]: 无法移动类型为 `dyn Animal` 的值
--> src/main.rs:25:9
|
25 | vec[1].make_sound()
| ^^^^^^^^^^^^^^^^^^^ `dyn Animal` 的大小无法在编译时确定
错误[E0507]: 无法从 `Vec<Box<dyn Animal>>` 的索引中移动值
--> src/main.rs:22:9
|
22 | vec[0].make_sound()
| ^^^^^^^------------
| | |
| | 由于此方法调用而移动了值
| 移动发生因为值的类型是 `dyn Animal`,它不实现 `Copy` 特性
|
提示: `Animal::make_sound` 接受了接收者 `self` 的所有权,因此移动了值
--> src/main.rs:5:19
|
5 | fn make_sound(self) -> String;
| ^^^^
错误[E0507]: 无法从 `Vec<Box<dyn Animal>>` 的索引中移动值
--> src/main.rs:25:9
|
25 | vec[1].make_sound()
| ^^^^^^^------------
| | |
| | 由于此方法调用而移动了值
| 移动发生因为值的类型是 `dyn Animal`,它不实现 `Copy` 特性
我不明白为什么会有这么多错误。
英文:
I'm struggling really hard with this error, here is my code:
struct Dog {}
struct Cat {}
trait Animal {
fn make_sound(self) -> String;
}
impl Animal for Dog {
fn make_sound(self) -> String {
String::from("woof")
}
}
impl Animal for Cat {
fn make_sound(self) -> String {
String::from("meow")
}
}
fn choose(vec: Vec<Box<dyn Animal>>, boo: bool) -> String {
if boo {
vec[0].make_sound()
}
else {
vec[1].make_sound()
}
}
Here is what the compiler says:
error[E0161]: cannot move a value of type `dyn Animal`
--> src/main.rs:22:9
|
22 | vec[0].make_sound()
| ^^^^^^^^^^^^^^^^^^^ the size of `dyn Animal` cannot be statically determined
error[E0161]: cannot move a value of type `dyn Animal`
--> src/main.rs:25:9
|
25 | vec[1].make_sound()
| ^^^^^^^^^^^^^^^^^^^ the size of `dyn Animal` cannot be statically determined
error[E0507]: cannot move out of index of `Vec<Box<dyn Animal>>`
--> src/main.rs:22:9
|
22 | vec[0].make_sound()
| ^^^^^^^------------
| | |
| | value moved due to this method call
| move occurs because value has type `dyn Animal`, which does not implement the `Copy` trait
|
note: `Animal::make_sound` takes ownership of the receiver `self`, which moves value
--> src/main.rs:5:19
|
5 | fn make_sound(self) -> String;
| ^^^^
error[E0507]: cannot move out of index of `Vec<Box<dyn Animal>>`
--> src/main.rs:25:9
|
25 | vec[1].make_sound()
| ^^^^^^^------------
| | |
| | value moved due to this method call
| move occurs because value has type `dyn Animal`, which does not implement the `Copy` trait
I don't get why there are so many errors.
答案1
得分: 2
When using trait objects, you can only call methods that take some kind of pointer to self
, such as the reference &self
. Any other methods (ones with a bare self
or where Self: Sized
) are excluded from the trait object interface. Check out the section on object safety.
Your method works fine with a shared reference, so you can add that in to get this working. (playground)
pub trait Animal {
fn make_sound(&self) -> String;
}
Why?
This limitation is for technical reasons. Trait objects have a virtual table that describes their trait methods. While these functions would be okay with taking self
, the non-dynamic code that calls them needs to know at compile time how to give them the self
argument.
When a method takes a reference &self
, this is fine. &self
compiles into a simple pointer, and &dyn Animal
compiles into two pointers, one to the value and one to the vtable. So the conversion from &dyn Animal
to &self
just keeps the pointer to the value.
On the other hand, self
can be any size. The size of a function's arguments determines how they're laid out in registers and on the stack, so there's no compile-time way for the correct arrangement to be set up in preparation for the virtual call.
This leaves one question: why not determine the layout at runtime? I haven't been able to find a definitive answer, but as far as I can tell, this is technically possible on an architecture level, but not implemented in any layer of the rust stack (rustc, llvm, linker), nor debug tools.
英文:
When using trait objects, you can only call methods that take some kind of pointer to self
, such as the reference &self
. Any other methods (ones with a bare self
or where Self: Sized
) are excluded from the trait object interface. Check out the section on object safety.
Your method works fine with a shared reference, so you can add that in to get this working. (playground)
pub trait Animal {
fn make_sound(&self) -> String;
}
Why?
This limitation is for technical reasons. Trait objects have a virtual table that describes their trait methods. While these functions would be okay with taking self
, the non-dynamic code that calls them needs to know at compile time how to give them the self
argument.
When a method takes a reference &self
, this is fine. &self
compiles into a simple pointer, and &dyn Animal
compiles into two pointers, one to the value and one to the vtable. So the conversion from &dyn Animal
to &self
just keeps the pointer to the value.
On the other hand, self
can be any size. The size of a function's arguments determines how they're laid out in registers and on the stack, so there's no compile-time way for the correct arrangement to be set up in preparation for the virtual call.
This leaves one question: why not determine the layout at runtime? I haven't been able to find a definitive answer, but as far as I can tell, this is technically possible on an architecture level, but not implemented in any layer of the rust stack (rustc, llvm, linker), nor debug tools.
答案2
得分: 0
It seems that you are having trouble understanding how data is passed around in rust. While other answers are correct, I will try to explain it in the simplest way I can.
我们似乎在理解Rust中数据传递方式方面遇到了一些困难。虽然其他回答都是正确的,但我将尽量以最简单的方式来解释它。
We all know that functions take arguments and use them to produce an output. There are several ways that function can handle these arguments:
我们都知道函数接受参数并使用它们生成输出。函数处理这些参数有几种方式:
-
Just look at the arguments, not modifying them, and produce output. This can be done using the
&
symbol.fn foo(arg: &ArgType) -> OutType
. We say that the functionfoo
borrows theArgType
. Real life example would be borrowing a notebook and using it in some way without modifying it in any way. -
只查看参数,不修改它们,并生成输出。可以使用
&
符号来实现这一点。fn foo(arg: &ArgType) -> OutType
。我们说函数foo
借用了ArgType
。现实生活中的例子是借用一本笔记本,并以某种方式使用它,而不以任何方式修改它。 -
Modify the arguments and produce output. The syntax for that is
fn foo(arg: &mut ArgType) -> OutType
. We say that the functionfoo
mutably borrows theArgType
. Real life example would be printing a document. You provide some text and a blank paper (mutable) and printer modifies it. -
修改参数并生成输出。其语法为
fn foo(arg: &mut ArgType) -> OutType
。我们说函数foo
可变借用了ArgType
。现实生活中的例子是打印文件。您提供一些文本和一张空白纸(可变的),打印机会对其进行修改。
If you know some other languages, you should be familiar with first two points. Now comes the part where things are different.
如果您了解其他一些编程语言,您应该熟悉前两个观点。现在是讲述不同之处的部分。
-
The arguments are given to the function and it can do whatever it wants with them. The syntax is
fn foo(arg: ArgType) -> OutType
. We say that thefoo
takes ownership or consumes the argument, or thearg
is moved to the function. In real life it would be like making a table out of wood. You get wood as input, you consume it to make a table. This is the most confusing part since other languages (like c++) make copies of data. In rust you have to be explicit when you want to make a copy. -
参数被传递给函数,它可以对它们进行任何操作。语法是
fn foo(arg: ArgType) -> OutType
。我们说foo
拥有或消耗了参数,或者arg
被移动到了函数中。在现实生活中,这就像用木头制作桌子。您将木头作为输入,然后消耗它以制作桌子。这是最令人困惑的部分,因为其他语言(如C++)会复制数据。在Rust中,您必须明确表示要进行复制。
Now to answer your question. The function make_sound
consumes self
to produce output. Now if we were to translate that to the real world example, it would be like an animal is transformed into a sound, never to be seen again. We also don't need to make a clone of an animal to transform it into a sound. We don't need to modify the animal in any way, therefore the simple borrow is the solution here. The correct signature would be fn make_sound(&self) -> String
现在回答您的问题。函数make_sound
会消耗self
来生成输出。现在,如果我们将其翻译成现实世界的例子,那就好比一个动物被转化成声音,永远不会再见。我们也不需要克隆一个动物以将其转化为声音。我们不需要以任何方式修改动物,因此简单的借用是解决方案。正确的签名应该是fn make_sound(&self) -> String
英文:
It seems that you are having trouble understanding how data is passed around in rust. While other answers are correct, I will try to explain it in the simplest way I can.
We all know that functions take arguments and use them to produce an output. There are several ways that function can handle these arguments:
-
Just look at the arguments, not modifying them, and produce output. This can be done using the
&
symbol.fn foo(arg: &ArgType) -> OutType
. We say that the functionfoo
borrows theArgType
. Real life example would be borrowing a notebook and using it in some way without modifying it in any way. -
Modify the arguments and produce output. The syntax for that is
fn foo(arg: &mut ArgType) -> OutType
. We say that the functionfoo
mutably borrows theArgType
. Real life example would be printing a document. You provide some text and a blank paper (mutable) and printer modifies it.
If you know some other languages, you should be familiar with first two points. Now comes the part where things are different.
- The arguments are given to the function and it can do whatever it wants with them. The syntax is
fn foo(arg: ArgType) -> OutType
. We say that thefoo
takes ownership or consumes the argument, or thearg
is moved to the function. In real life it would be like making a table out of wood. You get wood as input, you consume it to make a table. This is the most confusing part since other languages (like c++) make copies of data. In rust you have to be explicit when you want to make a copy.
Now to answer your question. The function make_sound
consumes self
to produce output. Now if we were to translate that to the real world example, it would be like an animal is transformed into a sound, never to be seen again. We also don't need to make a clone of an animal to transform it into a sound. We don't need to modify the animal in any way, therefore the simple borrow is the solution here. The correct signature would be fn make_sound(&self) -> String
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论