克隆具体类型的自定义结构体为特征对象

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

Clone custom structs of concrete type as trait objects

问题

使用Rc,我可以将具体类型的Rc转换为特征对象:

use std::rc::Rc;

trait Foo {}

impl Foo for usize {}

fn main() {
    let x: Rc<usize> = Rc::new(1);
    let y: Rc<dyn Foo> = x.clone();
}

如果我为Rc定义一个没有Sized边界的包装器,我也可以使用特征对象:

use std::rc::Rc;

trait Foo {}

#[derive(Clone)]
struct Wrapper<T: ?Sized>(Rc<T>);

impl Foo for usize {}

fn main() {
    let x: Wrapper<dyn Foo> = Wrapper(Rc::new(1));
}

然而,我不能将具体类型的包装器克隆为特征对象:

use std::rc::Rc;

trait Foo {}

#[derive(Clone)]
struct Wrapper<T: ?Sized>(Rc<T>);

impl Foo for usize {}

fn main() {
    let x: Wrapper<usize> = Wrapper(Rc::new(1));
    let y: Wrapper<dyn Foo> = x.clone(); // 这不会编译
}

编译器报错如下:

error[E0308]: mismatched types
  --> src/main.rs:13:31
   |
13 |     let y: Wrapper<dyn Foo> = x.clone();
   |            ----------------   ^^^^^^^^^ 预期特征对象 `dyn Foo`,找到 `usize`
   |            |
   |            预期是由此引起的结构体 `Wrapper<dyn Foo>`
              找到结构体 `Wrapper<usize>`

有关此错误的更多信息,请尝试运行 `rustc --explain E0308`。
error: 由于前一个错误,无法编译 `playground`

我有点困惑,因为我不理解为什么第三个示例不起作用。有人可以帮助我让第三个示例正常工作吗?我漏掉了什么?

英文:

Using Rc, I can cast an Rc of a concrete type to a trait object:

use std::rc::Rc;

trait Foo {}

impl Foo for usize {}

fn main() {
    let x: Rc&lt;usize&gt; = Rc::new(1);
    let y: Rc&lt;dyn Foo&gt; = x.clone();
}

Playground.

If I define a wrapper for an Rc without the Sized bound, I can also use trait objects:

use std::rc::Rc;

trait Foo {}

#[derive(Clone)]
struct Wrapper&lt;T: ?Sized&gt;(Rc&lt;T&gt;);


impl Foo for usize {}

fn main() {
    let x: Wrapper&lt;dyn Foo&gt; = Wrapper(Rc::new(1));
}

Playground.

However, I cannot clone a wrapper of a concrete type as a trait object:

use std::rc::Rc;

trait Foo {}

#[derive(Clone)]
struct Wrapper&lt;T: ?Sized&gt;(Rc&lt;T&gt;);


impl Foo for usize {}

fn main() {
    let x: Wrapper&lt;usize&gt; = Wrapper(Rc::new(1));
    let y: Wrapper&lt;dyn Foo&gt; = x.clone(); // this does not compile
}

The compiler complains with the following error:

error[E0308]: mismatched types
  --&gt; src/main.rs:13:31
   |
13 |     let y: Wrapper&lt;dyn Foo&gt; = x.clone();
   |            ----------------   ^^^^^^^^^ expected trait object `dyn Foo`, found `usize`
   |            |
   |            expected due to this
   |
   = note: expected struct `Wrapper&lt;dyn Foo&gt;`
              found struct `Wrapper&lt;usize&gt;`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error

Playground.

I'm a little bit confused, as I don't understand why the third example does not work. Can anyone help me to get the third example working? what am I missing?

答案1

得分: 0

这个其他示例可以工作:

use std::rc::Rc;

trait Foo {}

#[derive(Clone)]
struct Wrapper<T: ?Sized>(Rc<T>);

impl Foo for usize {}

fn main() {
    let x: Wrapper<usize> = Wrapper(Rc::new(1));
    let y: Wrapper<dyn Foo> = Wrapper(x.0.clone()); // 克隆内部的 Rc
}

Playground

虽然这对我来说足够了,但我认为它包含了太多样板代码。欢迎其他建议。

英文:

This other example works:

use std::rc::Rc;

trait Foo {}

#[derive(Clone)]
struct Wrapper&lt;T: ?Sized&gt;(Rc&lt;T&gt;);


impl Foo for usize {}

fn main() {
    let x: Wrapper&lt;usize&gt; = Wrapper(Rc::new(1));
    let y: Wrapper&lt;dyn Foo&gt; = Wrapper(x.0.clone()); // clone inner Rc
}

Playground.

While it is enough for me, I think that it has too much boilerplate code. Other suggestions are welcome.

答案2

得分: 0

你可以通过为你的 Wrapper 实现 CoerceUnsized 来实现对不定大小类型的强制转换,示例代码如下:

#![feature(unsize, coerce_unsized)]
use std::ops::CoerceUnsized;
use std::marker::Unsize;
impl<U: ?Sized, T: Unsize<U>> CoerceUnsized<Wrapper<U>> for Wrapper<T> {}

然后,你最初使用的语法将会编译通过:

fn main() {
    let x: Wrapper<usize> = Wrapper(Rc::new(1));
    let y: Wrapper<dyn Foo> = x.clone();
}

但由于你没有包含这个实现,编译器不知道你的类型可以被视为不定大小的。

注意:目前需要使用 unsizecoerce_unsized 特性,所以你必须使用 Nightly 编译器进行编译。

英文:

You can implement your own version of coercion to an unsized type by implementing CoerceUnsized for your Wrapper:

#![feature(unsize, coerce_unsized)]
use std::ops::CoerceUnsized;
use std::marker::Unsize;
impl&lt;U: ?Sized, T: Unsize&lt;U&gt;&gt; CoerceUnsized&lt;Wrapper&lt;U&gt;&gt; for Wrapper&lt;T&gt; {}

then the syntax you used initially does compile

fn main() {
    let x: Wrapper&lt;usize&gt; = Wrapper(Rc::new(1));
    let y: Wrapper&lt;dyn Foo&gt; = x.clone();
}

But since you did not include that implementation the compiler doesn't know that your type can be unsized.

Note: Right now that requires the features unsize and coerce_unsized though so you have to compile with a nightly compiler.

huangapple
  • 本文由 发表于 2023年3月7日 02:25:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/75654498.html
匿名

发表评论

匿名网友

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

确定