Как использовать объекты нестатической черты со связанными типами? - PullRequest
0 голосов
/ 28 мая 2018

У меня есть этот тип:

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

Я хочу реализовать std::ops::Index и возвращать ссылки на объекты признаков.Это была моя первая попытка ( Детская площадка ):

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.

Так что теперь я могу просто добавить 'static, связанный с моим параметром T,но это слишком ограничено, я думаю.Я легко могу реализовать такой метод, когда не использую связанный тип:

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

Нет обязательного ограничения 'static, потому что теперь сигнатура десугарсит до:

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

Что имеет смысл:объект черты должен жить как минимум 'a.( Игровая площадка со всем кодом ).


Но можно ли сделать эту работу, используя связанные типы (как с чертой Index)?У меня есть ощущение, что это может работать с родственными связанными типами, но (а) я не уверен и (б) они еще не реализованы.

Ответы [ 2 ]

0 голосов
/ 28 мая 2018

Возвращает ссылку на 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]
    }
}
0 голосов
/ 28 мая 2018

Одной из попыток является присоединение времени жизни к значению:

// 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

Естьнесколько решений, предложенных кодом ошибки 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, и дополнительное время жизни будет заражать везде ... Если это неприемлемо, вы можете либо

  • Не используйте черту Index, вместо этого введите собственную черту, чувствительную к жизни (таким образом, пользователям нужно будет использовать w.my_index(i) вместо &w[i]);или
  • Установите Output = Display + 'static и исключите все переходные типы.Пользователям нужно будет клонировать или использовать Rc.
...