简体   繁体   English

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

[英]How do I get a static path for tokio::fs::File::open?

The tokio::fs::File::open(path: T + 'static) requires a 'static lifetime on its path parameter. tokio::fs::File::open(path: T + 'static)在其path参数上需要'static生存期。

This makes sense because it is handled in runtime threads during the program's execution. 这是有道理的,因为它是在程序执行期间在运行时线程中处理的。 I think it would make more sense if you could pass your own lifetimes, because the runtime does not need to run the whole time and so you could throw away some stuff. 我认为,如果您可以度过自己的生命周期,那将更有意义,因为运行时不需要一直运行,因此您可以丢弃一些东西。 Do I understand something wrong? 我了解不对吗?

I'd like to stay for 'static at the moment and so my problem is this... 我想暂时保持'static ,所以我的问题是...

I have a trait TraitN and some struct StructX { path: String, } with a fn new(path: &String) -> Box<TraitN> . 我有一个trait TraitN和一些struct StructX { path: String, }和一个fn new(path: &String) -> Box<TraitN> The new creates and sets self.path = path.to_string(); new创建并设置了self.path = path.to_string(); .

In some impl fn doit(&self) { ... } for StructX , I'd like to call tokio::fs::File::open(&self.path) . 在一些针对StructX fn doit(&self) { ... }中,我想调用tokio::fs::File::open(&self.path)

How can I pass &self.path with a 'static lifetime? 如何通过&self.path传递'static生命周期”?

This is a complete example: 这是一个完整的示例:

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

playground 操场

TL;DR Use a String instead of a &str . TL; DR使用String而不是&str This might change when async / await syntax is stabilized. async / await语法稳定后,这可能会更改。


Here's the MCVE I made of your original question: 这是我对您的原始问题的回答:

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

To solve this, clone the String and give ownership of it to the function: 要解决此问题,请克隆String并将其所有权提供给函数:

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

With your example code, there are numerous problems: 在您的示例代码中,存在许多问题:

fn start_runtime() -> Result<()> {
    let path: &str = get_path().wait().unwrap().as_str();
    tokio::run(doit(path.clone()));
    Ok(())
}
  1. You cannot take a reference to the result of unwrap because nothing will own that value. 您不能引用unwrap的结果,因为没有任何东西拥有该值。 You cannot have a reference to this kind of temporary. 您无法引用此类临时文件。

  2. Cloning a &'a str returns a &'a str , not a String . 克隆&'a str返回一个&'a str ,而不是String

  3. It doesn't make sense to call wait on the value because that blocks the thread. 对值调用wait没有意义,因为这会阻塞线程。 Run everything in the reactor loop. 运行反应堆循环中的所有内容。

This function should look like 这个功能应该看起来像

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

Then all of your code should switch to impl Into<String> instead of &str of &'static str . 然后,所有的代码应切换到impl Into<String> ,而不是&str&'static str doit also needs to be able to create duplicate String s: doit还需要能够创建重复的String

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

this [...] is config which doesn't change [...] read from a configfile during app init. 此配置是在应用程序初始化期间不会更改从配置文件读取的配置。

In that case, create a singleton which will give you an effectively-static value: 在这种情况下, 创建一个单例将为您提供有效的静态值:

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

Then change all of the values to &'static str : 然后将所有值更改为&'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
        })
    }
}

And take a reference to the singleton: 并参考单例:

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

You can couple this with phimuemue's answer to get a &'static MyConfigStruct , which could then have a fn foo(&'static self) that is available. 您可以将其与phimuemue的答案结合使用以获得&'static MyConfigStruct ,然后可以使用fn foo(&'static self)


There must be something wrong with a language if this becomes so difficult and needs mem-io multiple times. 如果语言变得如此困难并且需要多次mem-io,则语言一定存在问题。

You are partially correct. 您部分正确。 It's difficult to have maximally performant async code with today's Rust (1.30) because Rust wants to ensure memory safety above all else. 今天的Rust(1.30)很难获得最高性能的异步代码,因为Rust想要确保内存安全高于一切。 This doesn't mean that the code is unperformant, just that there's a bit of room to do better. 这并不意味着代码性能不佳,只是还有一些地方可以做得更好。

Honestly, making clones here is unlikely to be a performance bottleneck, but it is annoying. 老实说,在这里进行克隆不太可能成为性能瓶颈,但这很烦人。 That's where async and await syntax comes in. This will allow futures to more easily make use of references in an idiomatic Rust manner. 这就是asyncawait语法的来源。这将使期货更容易以惯用的Rust方式使用引用。

because the runtime does not need to run the whole time [...] Do I understand something wrong? 因为运行时不需要一直运行[...]我是否理解错误?

However, async and await still might not help you, as by default Tokio will run your future on a different thread. 但是, asyncawait仍然可能无法帮助您,因为默认情况下,Tokio将在另一个线程上运行您的未来。 That's one of the primary reasons it requires a 'static bound. 这是它需要'static界限'static主要原因之一。 This prevents a Tokio thread from having a reference to a stack local that goes out of scope, introducing memory unsafety. 这样可以防止Tokio线程引用超出范围的本地堆栈,从而导致内存不安全。 This isn't a unique problem to Tokio, however. 但是,这并不是Tokio的独特问题。

See also: 也可以看看:

Other bits 其他位

It appears that every single call to wait in this code is a misuse of futures. 看起来, 每次 wait此代码的电话都是对期货的滥用。 You may wish to re-read the Tokio docs to better understand understand how you are supposed to chain futures. 您可能希望重新阅读Tokio文档,以更好地了解您应该如何链接期货。 If there's a wait call, it's usually at the end of everything, and even that is rare when using Tokio. 如果有一个wait呼叫,通常是一切都结束了,甚至在使用Tokio时也很少。

See also: 也可以看看:

You can restrict the lifetime of &self : 您可以限制&self的寿命:

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

If you do this, you may actually be better off to have StructX store the 'static borrow of the path in the first place (instead of a string). 如果这样做,让StructX存储路径'static借位(而不是字符串)可能会更好。

I can answer this now by myself: 我现在可以自己回答:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM