“Cannot move a value of dyn type” 翻译为:无法移动动态类型的值。

huangapple go评论69阅读模式
英文:

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) -&gt; String;
}

impl Animal for Dog {
    fn make_sound(self) -&gt; String {
        String::from(&quot;woof&quot;)
    }
}

impl Animal for Cat {
    fn make_sound(self) -&gt; String {
        String::from(&quot;meow&quot;)
    }
}

fn choose(vec: Vec&lt;Box&lt;dyn Animal&gt;&gt;, boo: bool) -&gt; 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`
  --&gt; 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`
  --&gt; 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&lt;Box&lt;dyn Animal&gt;&gt;`
  --&gt; 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
  --&gt; src/main.rs:5:19
   |
5  |     fn make_sound(self) -&gt; String;
   |                   ^^^^

error[E0507]: cannot move out of index of `Vec&lt;Box&lt;dyn Animal&gt;&gt;`
  --&gt; 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 &amp;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(&amp;self) -&gt; 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 &amp;self, this is fine. &amp;self compiles into a simple pointer, and &amp;dyn Animal compiles into two pointers, one to the value and one to the vtable. So the conversion from &amp;dyn Animal to &amp;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:

我们都知道函数接受参数并使用它们生成输出。函数处理这些参数有几种方式:

  1. Just look at the arguments, not modifying them, and produce output. This can be done using the &amp; symbol. fn foo(arg: &amp;ArgType) -&gt; OutType. We say that the function foo borrows the ArgType. Real life example would be borrowing a notebook and using it in some way without modifying it in any way.

  2. 只查看参数,不修改它们,并生成输出。可以使用&amp;符号来实现这一点。fn foo(arg: &amp;ArgType) -&gt; OutType。我们说函数foo借用了ArgType。现实生活中的例子是借用一本笔记本,并以某种方式使用它,而不以任何方式修改它。

  3. Modify the arguments and produce output. The syntax for that is fn foo(arg: &amp;mut ArgType) -&gt; OutType. We say that the function foo mutably borrows the ArgType. Real life example would be printing a document. You provide some text and a blank paper (mutable) and printer modifies it.

  4. 修改参数并生成输出。其语法为fn foo(arg: &amp;mut ArgType) -&gt; 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.

如果您了解其他一些编程语言,您应该熟悉前两个观点。现在是讲述不同之处的部分。

  1. The arguments are given to the function and it can do whatever it wants with them. The syntax is fn foo(arg: ArgType) -&gt; OutType. We say that the foo takes ownership or consumes the argument, or the arg 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.

  2. 参数被传递给函数,它可以对它们进行任何操作。语法是fn foo(arg: ArgType) -&gt; 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(&amp;self) -&gt; String

现在回答您的问题。函数make_sound会消耗self来生成输出。现在,如果我们将其翻译成现实世界的例子,那就好比一个动物被转化成声音,永远不会再见。我们也不需要克隆一个动物以将其转化为声音。我们不需要以任何方式修改动物,因此简单的借用是解决方案。正确的签名应该是fn make_sound(&amp;self) -&gt; 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:

  1. Just look at the arguments, not modifying them, and produce output. This can be done using the &amp; symbol. fn foo(arg: &amp;ArgType) -&gt; OutType. We say that the function foo borrows the ArgType. Real life example would be borrowing a notebook and using it in some way without modifying it in any way.

  2. Modify the arguments and produce output. The syntax for that is fn foo(arg: &amp;mut ArgType) -&gt; OutType. We say that the function foo mutably borrows the ArgType. 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.

  1. The arguments are given to the function and it can do whatever it wants with them. The syntax is fn foo(arg: ArgType) -&gt; OutType. We say that the foo takes ownership or consumes the argument, or the arg 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(&amp;self) -&gt; String

huangapple
  • 本文由 发表于 2023年4月17日 21:26:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76035679.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定