Как исправить: значение может содержать ссылки; добавить «статическую» привязку к `T`

#rust #object-lifetime

#Ржавчина #объект-время жизни

Вопрос:

Мне снова удалось столкнуться с проблемой на всю жизнь, которую я, похоже, не могу решить самостоятельно.

У меня есть эта черта

 pub trait MiddlewareHandler: Clone {
    fn invoke (amp;self, req: amp;Request, res: amp;mut Response) -> bool {
        true
    }

    // we need this because otherwise clone() would be ambiguous
    fn clone_box(amp;self) -> Box<MiddlewareHandler> { 
        box self.clone() as Box<MiddlewareHandler> 
    }
}

impl MiddlewareHandler for fn (req: amp;Request, res: amp;mut Response) -> bool {
    fn invoke(amp;self, req: amp;Request, res: amp;mut Response) -> bool{
        (*self).invoke(req, res)
    }
}

impl Clone for Box<MiddlewareHandler> {
    fn clone(amp;self) -> Box<MiddlewareHandler> { 
        self.clone_box() 
    }
}
  

Это я реализую для fn (req: amp;Request, res: amp;mut Response) -> bool того, чтобы иметь возможность одновременно использовать accept облегченные функции и более тяжелые MiddlewareHandler средства реализации.

Я сохраняю их как Vec<Box<MiddlewareHandler>>

 pub struct Middleware {
    handlers: Vec<Box<MiddlewareHandler>>
}
  

Теперь проблема в том, что компилятор кричит на меня прямо здесь:

     pub fn add<T: MiddlewareHandler> (amp;mut self, handler: T) {
        self.handlers.push(box handler);
    }
  

В нем говорится:

 error: value may contain references; add `'static` bound to `T`
       self.handlers.push(box handler);
  

Реализация должна быть очень похожа на используемую здесь:

https://github.com/iron/iron/blob/master/src/chain/stackchain.rs#L67

Однако, похоже, я не вижу разницы :-/

Если кто-нибудь захочет помочь мне, я отправил код на github в static ветку:

https://github.com/floor-org/floor/tree/static

Ответ №1:

Проблема здесь в том, что для безопасного создания объекта в штучной упаковке исходный объект не может иметь никаких параметров времени жизни (кроме static ), или сам объект также должен учитывать это время жизни, что в общем случае невозможно. Чтобы исправить это:

 pub fn add<T: MiddlewareHandler   'static> (amp;mut self, handler: T) {
    self.handlers.push(box handler);
}
  

Читается немного странно, но в нем говорится: « T необходимо реализовать MiddlewareHandler , и он не может содержать никаких ссылок, у которых нет времени static жизни». Это работает только для static .

Комментарии:

1. Я думаю, что я добавил ' static почти все места в этом фрагменте, но я, очевидно, пропустил попытку 'static там 😉 Но я не понимаю, почему это там не нужно: github.com/iron/iron/blob/master/src/chain/stackchain.rs#L67 Чем отличается их реализация в этом отношении?

2. Поскольку признак наследуется от Send и Send подразумевает 'static

3. Черт возьми! Я просмотрел документацию Send , но это не вызвало никаких вопросов, потому что я не создаю здесь никаких задач, поэтому это казалось излишним. Возможно, вы можете ответить на два дополнительных вопроса: pub trait MiddlewareHandler: Clone Send работает, но странно «злоупотреблять» Send этим. Почему я не могу использовать pub trait MiddlewareHandler: Clone 'static then? Вы сказали Send , что подразумевает 'static , но я посмотрел на источник doc.rust-lang.org/core/kinds/trait.Send.html и не могу найти никаких намеков на это. Я бы ожидал увидеть 'static где-нибудь там.

4.Типы немного странные, они прямо сейчас встроены в язык (хотя есть RFC, чтобы изменить это). Я ожидал Clone 'static бы, что сработает, мне грустно, что это не так.

5. Тогда хорошо. Было бы правильно вывести из send , если вы действительно просто хотите сделать свой тип статическим atm? Я хочу написать идиоматический Rust, и вывод из send него кажется ненужным, учитывая его описание. Не могли бы вы сделать то же самое здесь с текущим дизайном Rust?

Ответ №2:

Объект признака стирает тип внутренних данных, что означает, что он не известен, просто взглянув на объект Box<Trait> признака. В частности, любое время жизни данных также стирается, поэтому компилятор не может определить, содержит ли / когда объект признака ссылки на недопустимые данные (т. Е. Время жизни некоторой ссылки истекло), Поэтому Rust в настоящее время обеспечивает, чтобы любые данные в принадлежащем объекту признака никогда не должны «истекать». То есть в нем нет ссылок (ну, если быть точным, любые ссылки действительны вечно, т.Е. 'static ).

Это выражается через встроенную «черту» 'static :

 pub fn add<T: 'static   MiddlewareHandler>(...
  

(Это может измениться в будущем, поскольку принадлежащие объекты признаков могут быть ограничены реальным временем жизни, и поэтому хранение без 'static ссылок будет безопасным.)

Комментарии:

1. Отличный ответ! Спасибо за это. Я все еще удивляюсь, почему это работает для них: github.com/iron/iron/blob/master/src/chain/stackchain.rs#L67 Я не понимаю, чем их код отличается от моего. Кроме того, если я изменю код таким образом, я столкнусь со следующей ошибкой instantiating a type parameter with an incompatible type T, which does not fulfill 'static , так что, похоже, это указывает на то, что у меня ошибка где-то еще, может быть? : -S