Как мне получить статический путь для tokio :: fs :: File :: open? - PullRequest
0 голосов
/ 28 октября 2018

Для tokio::fs::File::open(path: T + 'static) требуется время жизни 'static для параметра path.

Это имеет смысл, поскольку оно обрабатывается в потоках времени выполнения во время выполнения программы.Я думаю, что было бы больше смысла, если бы вы могли пропустить свои собственные времена жизни, потому что среда выполнения не должна запускаться все время, и поэтому вы могли бы выбросить некоторые вещи.Я понимаю, что-то не так?

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

У меня есть trait TraitN и некоторые struct StructX { path: String, } с fn new(path: &String) -> Box<TraitN>.new создает и устанавливает self.path = path.to_string();.

В некотором значении fn doit(&self) { ... } для StructX, я хотел бы позвонить tokio::fs::File::open(&self.path).

Как я могу передать &self.path с 'static продолжительностью жизни?

Это полный пример:

extern crate futures;
extern crate tokio;
#[macro_use]
extern crate error_chain;

use futures::future;
use futures::future::{loop_fn, ok, Future, Loop};
use futures::Stream;
use std::io::BufReader;
use tokio::{fs, io};

mod error {
    error_chain!{}
}

use error::*;

type FutureResult<T> = future::FutureResult<T, Error>;

trait HandlerTrait {
    fn new(path: &str) -> Box<HandlerTrait>
    where
        Self: Sized;
    fn get_all(&self) -> FutureResult<Vec<String>>;
}

#[derive(Debug)]
pub struct Handler {
    path: String,
}

impl HandlerTrait for Handler {
    fn new(path: &str) -> Box<HandlerTrait> {
        Box::new(Handler {
            path: path.to_string(),
        })
    }

    fn get_all(&self) -> FutureResult<Vec<String>> {
        let file = fs::File::open(self.path.clone())
            .and_then(|file: fs::File| ok(file))
            .wait()
            .unwrap();
        let lines = io::lines(BufReader::new(file));
        ok(lines
            .filter(|line| line.len() > 80)
            .map(|all| all[0..80].to_string())
            .collect()
            .wait()
            .unwrap())
    }
}

fn get_handler(path: &str) -> Option<Box<HandlerTrait>> {
    Some(Handler::new(path))
}

fn get_path() -> FutureResult<String> {
    ok("./somepath/file".to_string())
}

fn start_runtime() -> Result<()> {
    let path: &str = get_path().wait().unwrap().as_str();
    tokio::run(doit(path.clone()));
    Ok(())
}

fn doit(path: &'static str) -> impl Future<Item = (), Error = ()> + 'static {
    let n = 0;
    loop_fn(n, move |_nr| {
        let lh = get_handler(path).unwrap();
        lh.get_all()
            .or_else(|_| Err(()))
            .and_then(|_all| ok(Loop::Break(())))
    })
}

#[test]
fn test() {
    start_runtime().unwrap();
    assert!(true);
}
error[E0597]: borrowed value does not live long enough
  --> src/lib.rs:63:22
   |
63 |     let path: &str = get_path().wait().unwrap().as_str();
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^         - temporary value only lives until here
   |                      |
   |                      temporary value does not live long enough
   |
   = note: borrowed value must be valid for the static lifetime...

детская площадка

Ответы [ 3 ]

0 голосов
/ 28 октября 2018

TL; DR Используйте String вместо &str.Это может измениться, когда синтаксис async / await стабилизируется.


Вот MCVE, который я сделал из вашего исходного вопроса:

extern crate tokio; // 0.1.11

trait TraitN {}

struct StructX {
    path: String,
}

impl TraitN for StructX {}

fn new(path: &str) -> Box<TraitN> {
    Box::new(StructX {
        path: path.to_string(),
    })
}

impl StructX {
    fn doit(&self) {
        tokio::fs::File::open(self.path.clone());
    }
}

Чтобы решить эту проблему, клонируйте String и передайте в собственность функции:

impl StructX {
    fn doit(&self) {
        tokio::fs::File::open(self.path.clone());
    }
}

С вашим примером кода есть множество проблем:

fn start_runtime() -> Result<()> {
    let path: &str = get_path().wait().unwrap().as_str();
    tokio::run(doit(path.clone()));
    Ok(())
}
  1. Вы не можетевозьмите ссылку на результат unwrap, потому что ничто не будет владеть этим значением.Вы не можете ссылаться на этот тип временных объектов.

  2. Клонирование &'a str возвращает &'a str, а не String.

  3. Не имеет смысла вызывать wait для значения, потому что это блокирует поток.Запустите все в контуре реактора.

Эта функция должна выглядеть следующим образом:

fn start_runtime() -> Result<()> {
    tokio::run({
        get_path()
            .map_err(|e| panic!("{}", e))
            .and_then(|path| doit(path))
    });
    Ok(())
}

Тогда весь ваш код должен переключиться на impl Into<String> вместо &str из&'static str.doit также должен иметь возможность создавать дубликаты String s:

fn doit(path: impl Into<String> + Clone) -> impl Future<Item = (), Error = ()> + 'static {
    let n = 0;
    let path = path.into();
    loop_fn(n, move |_nr| {
        let lh = get_handler(path.clone()).unwrap();
        lh.get_all()
            .or_else(|_| Err(()))
            .and_then(|_all| ok(Loop::Break(())))
    })
}

это [...] конфигурация, которая не изменяет [...] чтение из файла конфигурацииво время инициализации приложения.

В этом случае создайте синглтон , который даст вам фактически статическое значение:

extern crate lazy_static; // 1.1.0

use lazy_static::lazy_static;

lazy_static! {
    static ref PATH: String = {
        // Should be read from a file.
        String::from("/the/path/to/the/thing")
    };
}

Затем измените всезначения &'static str:

#[derive(Debug)]
pub struct Handler {
    path: &'static str,
}

impl HandlerTrait for Handler {
    fn new(path: &'static str) -> Box<HandlerTrait> {
        Box::new(Handler {
            path
        })
    }
}

И взять ссылку на синглтон:

fn start_runtime() -> Result<()> {
    tokio::run(doit(&PATH));
    Ok(())
}

Вы можете связать это с ответом phimuemue , чтобы получить &'static MyConfigStruct, который затем может иметь fn foo(&'static self), что доступно.


Должно быть что-то не так с языком, если это становится настолько сложным и требует многократного запоминания.

Вы частично правы.Трудно иметь максимально производительный асинхронный код с сегодняшним Rust (1.30), потому что Rust хочет обеспечить безопасность памяти превыше всего.Это не означает, что код не работает, просто есть немного места для улучшения.

Честно говоря, создание клонов здесь вряд ли будет узким местом для производительности, но это раздражает.Вот тут и вступают async и await синтаксис . Это позволит фьючерсам более легко использовать ссылки в идиоматическом стиле Rust.

, поскольку среда выполнения ненужно все время работать [...] Я понимаю, что что-то не так?

Однако async и await все еще могут не помочь вам, так как по умолчанию Токио будет работать на васдругая тема.Это одна из основных причин, по которой требуется ограничение 'static.Это препятствует тому, чтобы поток Tokio имел ссылку на локальный стек, выходящий из области видимости, создавая небезопасную память.Однако это не единственная проблема для Tokio.

См. Также:

Другие биты

Похоже, что каждый отдельный вызов wait в этом коде является неправильным использованием фьючерсов.Возможно, вы захотите перечитать Документы Tokio , чтобы лучше понять, как вы должны связывать будущее.Если есть вызов wait, он обычно в конце всего, и даже это редко при использовании Tokio.

См. Также:

0 голосов
/ 29 октября 2018

Я могу ответить на этот вопрос сам:

0 голосов
/ 28 октября 2018

Вы можете ограничить время жизни &self:

impl StructX {
    fn doit(&'static self) {
        // here, we know that self and its members are 'static
    }
}

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

...