如何匹配特征实现者 [英] How to match trait implementors

查看:34
本文介绍了如何匹配特征实现者的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个由一些结构实现的特性.我想写一个模式匹配,我可以处理所有可能的情况:

I have a trait that is implemented by some structs. I want to write a pattern match where I can handle every possible case:

trait Base {}

struct Foo {
    x: u32,
}
struct Bar {
    y: u32,
}

impl Base for Foo {}
impl Base for Bar {}

fn test(v: bool) -> Box<Base + 'static> {
    if v {
        Box::new(Foo { x: 5 })
    } else {
        Box::new(Bar { y: 10 })
    }
}

fn main() {
    let f: Box<Base> = test(true);

    match *f {
        Foo { x } => println!("it was Foo: {}!", x),
        Bar { y } => println!("it was Bar: {}!", y),
    }
}

(游乐场)

我收到此编译错误:

error[E0308]: mismatched types
  --> src/main.rs:25:9
   |
25 |         Foo { x } => println!("it was Foo: {}!", x),
   |         ^^^^^^^^^ expected trait Base, found struct `Foo`
   |
   = note: expected type `dyn Base`
              found type `Foo`

error[E0308]: mismatched types
  --> src/main.rs:26:9
   |
26 |         Bar { y } => println!("it was Bar: {}!", y),
   |         ^^^^^^^^^ expected trait Base, found struct `Bar`
   |
   = note: expected type `dyn Base`
              found type `Bar`

推荐答案

你不能.Traits 不支持向下转换 - Rust 不是基于继承/子类型的语言,它为您提供了另一组抽象.此外,您想要做的是不健全的 - 特征是开放的(每个人都可以为任何事情实现它们),因此即使在您的情况下 match *f 涵盖所有可能的情况,通常编译器也不能知道这一点.

You can't. Traits do not support downcasting - Rust is not inheritance/subtyping-based language, and it gives you another set of abstractions. Moreover, what you want to do is unsound - traits are open (everyone can implement them for anything), so even if in your case match *f covers all possible cases, in general the compiler can't know that.

这里有两种选择.如果您事先知道实现您的特征的结构集,只需使用枚举,这是一个完美的工具.它们允许您静态匹配一组封闭的变体:

You have two options here. If you know the set of structures implementing your trait in advance, just use enum, it's a perfect tool for this. They allow you to statically match on a closed set of variants:

enum FooBar {
    Foo(u32),
    Bar(u32),
}

fn test(v: bool) -> FooBar {
    if v {
        FooBar::Foo(5)
    } else {
        FooBar::Bar(10)
    }
}

fn main() {
    let f: FooBar = test(true);

    // Now that we have a `Box<Base>` (`*f` makes it a `Base`),
    // let's handle different cases:
    match f {
        FooBar::Foo(x) => println!("it was Foo: {}!", x),
        FooBar::Bar(y) => println!("it was Bar: {}!", y),
    }
}

(游乐场)

这是迄今为止最简单的方法,应该始终是首选.

This is by far the simplest way and it should always be preferred.

另一种方法是使用 Any 特性.它是一种从 trait 对象到常规类型的类型安全向下转换的工具:

Another way is to use Any trait. It is a facility for type-safe downcasting from trait objects to regular types:

use std::any::Any;

struct Foo {
    x: u32,
}
struct Bar {
    y: u32,
}

fn test(v: bool) -> Box<Any + 'static> {
    if v {
        Box::new(Foo { x: 5 })
    } else {
        Box::new(Bar { y: 10 })
    }
}

fn main() {
    let f: Box<Any> = test(true);

    match f.downcast_ref::<Foo>() {
        Some(&Foo { x }) => println!("it was Foo: {}!", x),
        None => match f.downcast_ref::<Bar>() {
            Some(&Bar { y }) => println!("it was Bar: {}!", y),
            None => unreachable!(),
        },
    }

    // it will be nicer when `if let` lands
    //    if let Some(ref Foo { x }) = f.downcast_ref::<Foo>() {
    //        println!("it was Foo: {}!", x);
    //    } else if let Some(ref Bar { y }) = f.downcast_ref::<Bar>() {
    //        println!("it was Bar: {}!", y);
    //    } else { unreachable!() }
}

(游乐场)

理想情况下应该可以这样写:

Ideally it should be possible to write something like this:

trait Base: Any {}

impl Base for Foo {}
impl Base for Bar {}

然后在代码中使用Base,但是现在不能做,因为trait继承不适用于trait对象(例如,不可能从BoxBase).

and then use Base in the code, but it can't be done now because trait inheritance does not work with trait objects (e.g it is impossible to go from Box<Base> to Base<Any>).

这篇关于如何匹配特征实现者的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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