如何通过引用Rust中的特征实现来制作静态数组?

时间:2019-05-30 20:26:29

标签: rust

我希望能够遍历解析器数组,以尝试从主解析函数中查找委托解析器。解析器列表在编译时是已知的,因此我希望它是一个常量。

我尝试了一些这种方法,但是我无法使它起作用:

const ALL_PARSERS: [&Parser; 1] = [&CommentParser {}];

我该如何实现?

注意:

  • Parser是一个特质。
  • CommentParser是实现Parser的结构。
  • 还有Parser的其他实现,但为简单起见未显示。
  • 即使所有解析器实现在编译时都是已知的,但我只是想避免显式地一一尝试,因为那样会使代码比我认为的糟糕。

我当前遇到的错误:

|
11 | const ALL_PARSERS: [&Parser; 1] = [&CommentParser {}];
   |                    ^^^^^^^^^^^^ the trait `parsers::Parser` cannot be made into an object
   |
   = note: method `parse` has generic type parameters

我在解析方法中看不到任何泛型:

pub trait Parser {
    fn opening_char(self: &Self) -> char;
    fn parse(&mut self, env: impl ParserEnv) -> ParseResult;
}

如果我输入类型值:

const ALL_PARSERS: [Parser; 1] = [CommentParser {}];

错误变为:

11 | const ALL_PARSERS: [Parser; 1] = [CommentParser {}];
|                    ^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `(dyn parsers::Parser + 'static)`

1 个答案:

答案 0 :(得分:2)

特质引用需要标记为&dyn,例如&dyn Parser

trait Parser { }

struct CommentParser { }
impl Parser for CommentParser { }

const ALL_PARSERS: [&dyn Parser; 1] = [&CommentParser {}];

fn main() {
    for &parser in &ALL_PARSERS {
        // do something with parser
    }
}

Link to playground example


此外,如相关问题的this answer所述,如果要进行特征引用,则特征中不能包含通用参数,因此您需要向该特征中添加通用类型特征本身,或在分析器类型中使用特征引用而不是impl

// original (with error)
trait Parser {
    // impl ParserEnv is an implicit generic type
    fn parse(&mut self, env: impl ParserEnv) -> ParseResult;

    // same as:
    //   fn parser<E: ParserEnv>(&mut self, env: E) -> ParserResult;
}

// alternative 1, with trait generic type
trait Parser<E: ParserEnv> {
    fn parse(&mut self, env: E) -> ParseResult;
}

// alternative 2, with trait reference
trait Parser {
    fn parser(&mut self, env: &dyn ParserEnv) -> ParserResult;
    // may need &dyn mut ParserEnv if you want to modify env as well
}

我认为第二种方法可能是最好的,因为这样您就可以将解析器存储在数组中,而无需为解析器分配特定的ParserEnv类型。