如何拆箱包含在多态向量中的元素?

How to unbox elements contained in polymorphic vectors?

看完this answer to "Vector of objects belonging to a trait",Rust好像是自动拆箱的。是这样吗?

我的代码无法编译,我不明白答案的代码如何编译。

将多态向量的元素拆箱的正确方法是什么,其中包含已装箱的特征?

我读过 Rust by Example and the Box documentation 但我看不到任何看起来像 unbox() 的方法。

我的代码是:

trait HasArea {
    fn area(&self) -> f64;
}

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl HasArea for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

struct Square {
    x: f64,
    y: f64,
    side: f64,
}

impl HasArea for Square {
    fn area(&self) -> f64 {
        self.side * self.side
    }
}

fn print_area<T: HasArea>(shape: T) {
    println!("This shape has an area of {}", shape.area());
}

fn main() {
    let c = Circle {
        x: 0.0f64,
        y: 0.0f64,
        radius: 1.0f64,
    };

    let s = Square {
        x: 0.0f64,
        y: 0.0f64,
        side: 1.0f64,
    };

    print_area(c);
    print_area(s);

    let vec: Vec<Box<HasArea>> = Vec::new();
    vec.push(Box::new(c));
    vec.push(Box::new(s));

    for x in vec {
        print_area(x)
    }
}

我的错误是:

   Compiling rustgraph v0.1.0 (file:///home/chris/lunch/rustgraph)
error[E0277]: the trait bound `Box<HasArea>: HasArea` is not satisfied
  --> src/main.rs:54:9
   |
54 |         print_area(x)
   |         ^^^^^^^^^^ the trait `HasArea` is not implemented for `Box<HasArea>`
   |
   = note: required by `print_area`

您可以像 print_area(*x) 那样取消引用它,但由于其他原因它不起作用:Sized 绑定到 print_area 参数。您的函数需要知道其参数的大小。

您的代码中还有其他问题:您正试图推入一个不可变的向量,并且您正试图将移动的值装箱。这些在您在 print_area().

中使用后被移动了

我的意见是使 print_area 成为一个采用不可变引用的方法会更容易。这将按您预期的那样工作。

trait HasArea {
    fn area(&self) -> f64;
    fn print_area(&self) {
        println!("This shape has area of {}", self.area());
    }
}

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl HasArea for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

struct Square {
    x: f64,
    y: f64,
    side: f64,
}

impl HasArea for Square {
    fn area(&self) -> f64 {
        self.side * self.side
    }
}

fn print_area<T: HasArea>(shape: &T) {
    println!("This shape has an area of {}", shape.area());
}

fn main() {
    let c = Circle {
        x: 0.0f64,
        y: 0.0f64,
        radius: 1.0f64,
    };

    let s = Square {
        x: 0.0f64,
        y: 0.0f64,
        side: 1.0f64,
    };

    c.print_area();
    s.print_area();

    let mut vec: Vec<Box<HasArea>> = Vec::new();
    vec.push(Box::new(c));
    vec.push(Box::new(s));

    for x in vec {
        x.print_area();
    }
}

After reading it looks like Rust does automatic unboxing. Is this the case?

并不像您想象的那样自动。事实上,您正在寻找 unbox 方法,而 Box<T> 为目标 T 实现 Deref。这意味着您应该调用 as_ref() 或依赖 Deref 强制转换。请注意,T 对于未确定大小的类型是不可能的,并且由于您依赖于多态类型,因此使用者函数将必须接受引用。

我冒昧地修复了 mainprint_area 以使其工作。该向量也被错误地声明为不可变。

fn print_area<T: HasArea + ?Sized>(shape: &T) {
    println!("This shape has an area of {}", shape.area());
}

fn main() {
    let c = Circle {
        x: 0.0f64,
        y: 0.0f64,
        radius: 1.0f64,
    };

    let s = Square {
        x: 0.0f64,
        y: 0.0f64,
        side: 1.0f64,
    };

    print_area(&c);
    print_area(&s);

    let mut vec: Vec<Box<HasArea>> = Vec::new();
    vec.push(Box::new(c));
    vec.push(Box::new(s));

    for x in vec {
        print_area(&*x)
    }
}

作为 E_net4 建议的替代方案,您可以使用带有引用的 Vec 来代替装箱您的特征:

fn print_area<T: HasArea+?Sized>(shape: &T) {
    println!("This shape has an area of {}", shape.area());
}

let mut vec: Vec<&HasArea> = Vec::new();
vec.push(&c);
vec.push(&s);

for x in vec {
    print_area(x)
}

回答您的直接问题:

How to unbox elements contained in polymorphic vectors?

不能。一旦装箱并删除了具体类型,就是这样。 Box<SomeTrait> 不能变回 SomeConcreteType,因为没有人知道具体类型是什么。


解决代码中的问题...再次查看错误信息:

the trait bound Box<HasArea>: HasArea is not satisfied

那是因为对特征的引用(或特征框)

为了让你的程序像你最初写的那样编译和运行,你只需要实现盒子的特征,我们不妨也做参考:

impl<T: ?Sized> HasArea for Box<T>
    where T: HasArea
{
    fn area(&self) -> f64 { (**self).area() }    
}

impl<'a, T: ?Sized> HasArea for &'a T
    where T: HasArea
{
    fn area(&self) -> f64 { (**self).area() }    
}

这允许您的固定主线 运行:

fn main() {
    let c = Circle {
        x: 0.0f64,
        y: 0.0f64,
        radius: 1.0f64,
    };

    let s = Square {
        x: 0.0f64,
        y: 0.0f64,
        side: 1.0f64,
    };

    print_area(&c);
    print_area(&s);

    let vec: Vec<Box<HasArea>> = vec![Box::new(c), Box::new(s)];

    for x in vec {
        print_area(x)
    }
}

在这里,我们将 cs 的引用传递给 print_area,以避免转移所有权。我们还使用 vec! 宏以更少的仪式构造向量。