代码之家  ›  专栏  ›  技术社区  ›  Lukas Kalbertodt

如何使用关联类型的非静态特征对象?

  •  4
  • Lukas Kalbertodt  · 技术社区  · 6 年前

    我有这种类型:

    struct Wrap<T>(Vec<T>);
    

    我想实施 std::ops::Index 并返回对特征对象的引用。这是我第一次尝试( Playground ):

    use std::ops::Index;
    use std::fmt::Display;
    
    
    impl<T> Index<usize> for Wrap<T>
    where
        T: Display
    {
        type Output = Display;
        fn index(&self, index: usize) -> &Self::Output {
            &self.0[index]
        }
    }
    

    这不起作用,导致此错误:

    error[E0310]: the parameter type `T` may not live long enough
      --> src/main.rs:13:9
       |
    7  | impl<T> Index<usize> for Wrap<T>
       |      - help: consider adding an explicit lifetime bound `T: 'static`...
    ...
    13 |         &self.0[index]
       |         ^^^^^^^^^^^^^^
       |
    note: ...so that the type `T` will meet its required lifetime bounds
      --> src/main.rs:13:9
       |
    13 |         &self.0[index]
       |         ^^^^^^^^^^^^^^
    

    我想我知道为什么会这样: type Output = Display 等于 type Output = Display + 'static 因为每个特征对象都有一个寿命限制,默认为 'static .

    所以现在我可以添加 '静态 绑定到我的参数 T 但我认为这是过度约束的。当不使用关联类型时,我可以轻松实现这种方法:

    impl<T> Wrap<T>
    where
        T: Display,
    {
        fn my_index(&self, index: usize) -> &Display {
            &self.0[index]
        }
    }
    

    '静态 需要绑定,因为现在签名减影到:

    fn my_index<'a>(&'a self, index: usize) -> &'a Display + 'a
    

    这是有道理的:特质对象至少要活下去 'a . ( Playground with all the code )


    但是我能用相关的类型吗 (就像 Index 特征)?我觉得这可能适用于一般关联类型,但是(a)我不确定,(b)它们还没有实现。

    2 回复  |  直到 6 年前
        1
  •  5
  •   kennytm    6 年前

    一种尝试是将生存期附加到IMPL:

    // Note: won't work.
    
    impl<'a, T> Index<usize> for Wrap<T>
    where
        T: Display + 'a,
    {
        type Output = Display + 'a;
        fn index(&self, index: usize) -> &Self::Output {
            &self.0[index]
        }
    }
    

    但是,编译器不会接受它,因为 'a 未使用。

    error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
     --> src/main.rs:7:6
      |
    7 | impl<'a, T> Index<usize> for Wrap<T>
      |      ^^ unconstrained lifetime parameter
    

    有几个解决方案由 error code E0207 但是因为我们不能改变 Index 唯一可接受的解决办法是 Wrap 捕获不受约束的生存期参数:

    use std::ops::Index;
    use std::fmt::Display;
    use std::marker::PhantomData;
    
    struct Wrap<'a, T>(Vec<T>, PhantomData<&'a ()>);
    //          ^~             ^~~~~~~~~~~~~~~~~~~
    
    impl<'a, T> Index<usize> for Wrap<'a, T>
    where
        T: Display + 'a,
    {
        type Output = Display + 'a;
        fn index(&self, index: usize) -> &Self::Output {
            &self.0[index]
        }
    }
    
    fn main() {
        let w = Wrap(vec!['a', 'b'], PhantomData);
        println!("{}", &w[0]); // prints "a"
    
        let s = "hi".to_string();
        let w = Wrap(vec![&s], PhantomData);
        println!("{}", &w[0]); // prints "hi"
    }
    

    ( Playground )

    当然,这会改变你的API,延长的生命会感染任何地方…如果这是不可接受的,你也可以

    • 不使用 索引 特性,引入你自己的终身敏感特性(因此用户需要使用 w.my_index(i) 而不是 &w[i] ;或
    • 集合 Output = Display + 'static Rc .
        2
  •  0
  •   Shepmaster Tim Diekmann    6 年前

    返回对的引用 T 而不是返回对特征对象的引用并让用户强制转换为特征对象,或者只是让rust隐式地从上下文中推断何时执行转换:

    use std::fmt::Display;
    use std::ops::Index;
    
    fn main() {
        let w1 = Wrap(vec!['I', 'b']);
        let s = "am".to_string();
        let w2 = Wrap(vec![&s]);
        let w3 = Wrap(vec![1, 2]);
    
        let mut trait_store: Vec<Box<Display>> = Vec::new();
    
        trait_store.push(Box::new(w1.index(0)));
        trait_store.push(Box::new(w2.index(0)));
        trait_store.push(Box::new(w3.index(0)));
    
        for el in trait_store {
            println!("{}", el);
        }
    }
    
    struct Wrap<T>(Vec<T>);
    
    impl<T> Index<usize> for Wrap<T> {
        type Output = T;
        fn index(&self, index: usize) -> &Self::Output {
            &self.0[index]
        }
    }