Почему закрытие хода не захватит значение с универсальным типом? - PullRequest
0 голосов
/ 27 мая 2019

Я пытаюсь создать тип ScopeRunner, который может хранить вызовы методов для методов типов, реализующих черту Scope, например:

trait Scope {
    fn run(&self) -> String;
}

struct ScopeImpl;

impl Scope for ScopeImpl {
    fn run(&self) -> String {
        "Some string".to_string()
    }
}


struct ScopeRunner {
    runner: Box<dyn Fn() -> String>,
}

impl ScopeRunner {
    fn new<S: Scope>(scope: S) -> Self {
        ScopeRunner {
            runner: Box::new(move || scope.run())
        }
    }

    pub fn run(self) -> String {
        (self.runner)()
    }

}


fn main() {
    let scope = ScopeImpl {};
    let scope_runner = ScopeRunner::new(scope);

    dbg!(scope_runner.run());
}

Я ожидаю, что, поскольку ScopeRunner::new создает закрытие перемещенияэто приведет к перемещению области действия в замыкание.Но вместо этого средство проверки заимствований выдает мне эту ошибку:

error[E0310]: the parameter type `S` may not live long enough
  --> src/main.rs:21:30
   |
20 |     fn new<S: Scope>(scope: S) -> Self {
   |            -- help: consider adding an explicit lifetime bound `S: 'static`...
21 |         ScopeRunner {runner: Box::new(move || scope.run())}
   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: ...so that the type `[closure@src/main.rs:21:39: 21:58 scope:S]` will meet its required lifetime bounds
  --> src/main.rs:21:30
   |
21 |         ScopeRunner {runner: Box::new(move || scope.run())}
   | 

Когда я заменяю ScopeRunner::new не универсальной версией, которая просто принимает ScopeImpl, этот код работает.

fn new(scope: ScopeImpl) -> Self {
    ScopeRunner {
        runner: Box::new(move || scope.run())
    }
}

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

1 Ответ

3 голосов
/ 27 мая 2019

Проблема в том, что S может быть любого типа с Scope impl, который включает в себя все виды еще не существующих типов, которые содержат ссылки на другие типы. Например, у вас может быть такая реализация:

struct AnotherScope<'a> {
    reference: &'str,
}

impl Scope for ScopeImpl {
    fn run(&self) -> String {
        self.reference.to_string()
    }
}

Rust осторожен и хочет убедиться, что это будет работать для любой квалификации S, в том числе, если он содержит ссылки.

Самое простое решение - сделать, как указано в примечании об ошибке, и просто запретить S иметь нестатические ссылки:

fn new<S: Scope + 'static>(scope: S) -> Self {
    ScopeRunner {
        runner: Box::new(move || scope.run())
    }
}

Связывание S с 'static фактически означает, что S может содержать либо ссылки на значения со временем жизни 'static, либо никаких ссылок вообще.

Если вы хотите быть более гибким, вы можете расширить его до ссылок, которые переживают сам ScopeRunner:

struct ScopeRunner<'s> {
    runner: Box<dyn Fn() -> String + 's>,
}

impl<'s> ScopeRunner<'s> {
    fn new<S: Scope + 's>(scope: S) -> Self {
        ScopeRunner { 
            runner: Box::new(move || scope.run())
        }
    }

    pub fn run(self) -> String {
        (self.runner)()
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...