如何用可以是多种特征对象的参数定义函数? [英] How can I define a function with a parameter that can be multiple kinds of trait objects?

查看:74
本文介绍了如何用可以是多种特征对象的参数定义函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试定义一个将引用作为参数的函数,并在引用的对象上调用 generic 方法,并传递一个具体值.我需要一种方法,要求传递给我的函数的参数的泛型类型是函数将与之一起使用的具体类型的特征.我似乎无法解决该怎么做.

I'm trying to define a function that will take a reference as a parameter, and call a generic method on the referenced object, passing in a concrete value. I need a way of requiring that the generic type of the parameter passed to my function is a trait of the concrete type that the function will use it with. I can't seem to work out how to do this.

我要实现的目标的一个最小示例:

A minimal example of the sort of thing I'm trying to achieve:

trait Vehicle {}
trait Floating {}

struct Boat;
impl Vehicle for Boat {}
impl Floating for Boat {}

fn main() {
    let mut a: Vec<Box<dyn Vehicle>> = vec![];
    populate(&mut a); // Does not compile

    let mut b: Vec<Box<dyn Floating>> = vec![];
    populate(&mut b); // Also does not compile
}

fn populate(receiver: &mut Vec<Box<Boat>>) { // What should I put here?
    receiver.push(Box::new(Boat{}));
}

尝试编译它会出现以下错误:

Trying to compile this gives the following errors:

error[E0308]: mismatched types
  --> src/main.rs:10:14
   |
10 |     populate(&mut a); // Does not compile
   |              ^^^^^^ expected struct `Boat`, found trait object `dyn Vehicle`
   |
   = note: expected mutable reference `&mut std::vec::Vec<std::boxed::Box<Boat>>`
              found mutable reference `&mut std::vec::Vec<std::boxed::Box<dyn Vehicle>>`

error[E0308]: mismatched types
  --> src/main.rs:13:14
   |
13 |     populate(&mut b); // Also does not compile
   |              ^^^^^^ expected struct `Boat`, found trait object `dyn Floating`
   |
   = note: expected mutable reference `&mut std::vec::Vec<std::boxed::Box<Boat>>`
              found mutable reference `&mut std::vec::Vec<std::boxed::Box<dyn Floating>>`

我没想到会编译它,但是我不知道如何更改populate的签名.我来自Java领域,在那里我将使用有界通配符(例如void populate(List<? super Boat> receiver))来实现此目标,但是我找不到任何暗示Rust提供相同语义的信息.

I didn't expect this to compile, but I don't know how to change the signature of populate so that it will. I come from Java land, where I would achieve this using this using a bounded wildcard (e.g. void populate(List<? super Boat> receiver)), but I can't find anything to suggest that Rust offers equivalent semantics.

我该如何在这里修正populate的定义?

How might I go about fixing my definition of populate here?

我是Rust的新手,所以如果我完全吠错了树,请多包涵.我四处搜寻,似乎找不到如何实施此模式的示例.

I'm new to Rust, so bear with me if I'm completely barking up the wrong tree. I've searched around, and can't seem to find an example of how this pattern should be implemented.

推荐答案

稳定Rust

您可以为感兴趣的每个唯一特征对象创建并实现一个特征:

Stable Rust

You can create and implement a trait for every unique trait object you are interested in:

trait Shipyard {
    fn construct(boat: Boat) -> Box<Self>;
}

impl Shipyard for Boat {
    fn construct(boat: Boat) -> Box<Self> {
        Box::new(boat)
    }
}

impl Shipyard for dyn Vehicle {
    fn construct(boat: Boat) -> Box<dyn Vehicle> {
        Box::new(boat) as Box<dyn Vehicle>
    }
}

impl Shipyard for dyn Floating {
    fn construct(boat: Boat) -> Box<dyn Floating> {
        Box::new(boat) as Box<dyn Floating>
    }
}

fn populate<T: ?Sized>(receiver: &mut Vec<Box<T>>)
where
    T: Shipyard,
{
    receiver.push(T::construct(Boat));
}

宏可以删除重复项.

您可以使用不稳定的 CoerceUnsized 特征:

You can use the unstable CoerceUnsized trait:

#![feature(coerce_unsized)]

use std::ops::CoerceUnsized;

fn populate<T: ?Sized>(receiver: &mut Vec<Box<T>>)
where
    Box<Boat>: CoerceUnsized<Box<T>>,
{
    receiver.push(Box::new(Boat) as Box<T>);
}

等效地:

#![feature(unsize)]

use std::marker::Unsize;

fn populate<T: ?Sized>(receiver: &mut Vec<Box<T>>)
where
    Boat: Unsize<T>,
{
    receiver.push(Box::new(Boat) as Box<T>);
}

您可以在问题27732 中跟踪其稳定性.

You can track their stabilization in issue 27732.

此代码仅 能够创建特征对象,并且无法直接返回该结构:

This code is only able to create a trait object, and cannot return the struct directly:

let mut b: Vec<Box<Boat>> = vec![];
populate(&mut b);

error[E0277]: the trait bound `Boat: std::marker::Unsize<Boat>` is not satisfied
  --> src/main.rs:17:5
   |
17 |     populate(&mut b);
   |     ^^^^^^^^ the trait `std::marker::Unsize<Boat>` is not implemented for `Boat`
   |
   = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<std::boxed::Box<Boat>>` for `std::boxed::Box<Boat>`
note: required by `populate`
  --> src/main.rs:25:5
   |
25 | /     fn populate<T: ?Sized>(receiver: &mut Vec<Box<T>>)
26 | |     where
27 | |         Box<Boat>: CoerceUnsized<Box<T>>,
28 | |     {
29 | |         receiver.push(Box::new(Boat) as Box<T>);
30 | |     }
   | |_____^

要解决此问题,您可以像创建稳定Rust一样创建一个特征,但是该特征可以对所有特征对象进行全面实现:

To work around this, you can create a trait like we did for stable Rust, but this one can have a blanket implementation for all trait objects:

#![feature(unsize)]

use std::marker::Unsize;

trait Shipyard {
    fn construct(boat: Boat) -> Box<Self>;
}

impl Shipyard for Boat {
    fn construct(boat: Boat) -> Box<Self> {
        Box::new(boat)
    }
}

impl<U: ?Sized> Shipyard for U
where
    Boat: Unsize<U>,
{
    fn construct(boat: Boat) -> Box<Self> {
        Box::new(boat) as Box<U>
    }
}

fn populate<T: ?Sized>(receiver: &mut Vec<Box<T>>)
where
    T: Shipyard,
{
    receiver.push(T::construct(Boat));
}

感谢自然神指向我这些特征 查看全文

登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆