如何获取tokio :: fs :: File :: open的静态路径?

时间:2018-10-28 16:43:16

标签: rust rust-tokio

tokio::fs::File::open(path: T + 'static)的{​​{1}}参数需要一个'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);
}

playground

3 个答案:

答案 0 :(得分:1)

TL; DR使用String代替&str。当async / await语法稳定后,情况可能会改变。


这是我对您的原始问题的回答:

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>的{​​{1}}而不是&str&'static str还需要能够创建重复的doit

String
  

此[...]配置是在应用程序初始化期间不会更改从配置文件读取的配置。

在这种情况下,create a singleton将为您提供有效的静态值:

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
        })
    }
}

您可以将其与phimuemue's answer结合使用以获得fn start_runtime() -> Result<()> { tokio::run(doit(&PATH)); Ok(()) } ,然后可以使用&'static MyConfigStruct


  

如果语言变得如此困难并且需要多次mem-io,则语言一定存在问题。

您部分正确。今天的Rust(1.30)很难获得最高性能的异步代码,因为Rust想要确保内存安全高于一切。这并不意味着代码性能不佳,只是还有一些地方可以做得更好。

老实说,在这里进行克隆不太可能成为性能瓶颈,但这很烦人。这就是async and await syntax出现的地方。这将使期货更容易以惯用的Rust方式使用引用。

  

因为运行时不需要一直运行[...]我是否理解错误?

但是,fn foo(&'static self)async仍然可能对您没有帮助,因为默认情况下Tokio将在另一个线程上运行您的未来。这是需要绑定await的主要原因之一。这样可以防止Tokio线程引用超出范围的本地堆栈,从而导致内存不安全。但是,这对于Tokio来说并不是唯一的问题。

另请参阅:

其他位

此代码中对'static每一个调用似乎是对期货的滥用。您可能希望重新阅读the Tokio docs,以更好地了解您应该如何链接期货。如果有一个wait调用,通常会在所有操作结束时使用,甚至在使用Tokio时也很少见。

另请参阅:

答案 1 :(得分:0)

您可以限制&self的生存期:

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

如果这样做,将StructX的{​​{1}}路径借位存储在第一位(而不是字符串)实际上可能更好。

答案 2 :(得分:0)

我现在可以自己回答: