如何获得 tokio::fs::File::open 的静态路径?
How do I get a static path for tokio::fs::File::open?
tokio::fs::File::open(path: T + 'static)
的 path
参数需要 'static
生命周期。
这是有道理的,因为它是在程序执行期间在 运行时间线程中处理的。我认为如果你能度过自己的一生会更有意义,因为 运行 时间不需要一直 运行 所以你可以扔掉一些东西。我是不是理解错了?
我现在想待 'static
,所以我的问题是……
我有一个 trait TraitN
和一些 struct StructX { path: String, }
有一个 fn new(path: &String) -> Box<TraitN>
。 new
创建并设置 self.path = path.to_string();
.
在 StructX
的一些实现 fn doit(&self) { ... }
中,我想调用 tokio::fs::File::open(&self.path)
.
如何在 'static
生命周期内通过 &self.path
?
这是一个完整的例子:
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...
您可以限制 &self
的生命周期:
impl StructX {
fn doit(&'static self) {
// here, we know that self and its members are 'static
}
}
如果你这样做,你实际上可能更好地让 StructX
首先存储路径的 'static
借用(而不是字符串)。
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(())
}
您不能引用 unwrap
的结果,因为没有任何东西拥有该值。你不能引用这种临时文件。
克隆 &'a str
returns &'a str
,而不是 String
.
对值调用 wait
没有意义,因为它会阻塞线程。 运行 反应堆循环中的所有内容。
这个函数应该看起来像
fn start_runtime() -> Result<()> {
tokio::run({
get_path()
.map_err(|e| panic!("{}", e))
.and_then(|path| doit(path))
});
Ok(())
}
那么你所有的代码都应该切换到 impl Into<String>
而不是 &'static str
的 &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(())))
})
}
this [...] is config which doesn't change [...] read from a configfile during app init.
在这种情况下,create a singleton 将为您提供有效静态值:
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(())
}
您可以将其与 结合使用以获得 &'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.
你说对了一部分。今天的 Rust (1.30) 很难拥有最高性能的异步代码,因为 Rust 想要确保内存安全高于一切。这并不意味着代码性能不佳,只是还有改进的余地。
老实说,在这里克隆不太可能成为性能瓶颈,但很烦人。这就是 async
and await
syntax 的用武之地。这将使 futures 能够更轻松地以惯用的 Rust 方式使用引用。
because the runtime does not need to run the whole time [...] Do I understand something wrong?
但是,async
和 await
仍然可能无法帮助您,因为默认情况下 Tokio 将 运行 您的未来放在不同的线程上。这是它需要 'static
绑定的主要原因之一。这可以防止 Tokio 线程引用超出范围的本地堆栈,从而引入内存不安全。然而,这并不是 Tokio 独有的问题。
另请参阅:
- How can I pass a reference to a stack variable to a thread?
其他位
似乎 在此代码中对 wait
的每个 调用都是对期货的滥用。您可能希望重新阅读 the Tokio docs 以更好地理解您应该如何链接期货。如果有一个 wait
调用,它通常在所有内容的末尾,即使在使用 Tokio 时也很少见。
另请参阅:
我现在可以自己回答了:
fn make_static_str<T>(s: T) -> &'static str where T: Into<String>
Arc<Mutex<String>>
的解决方案 - 测试失败,因为在 playground 上没有可读取的文件。
tokio::fs::File::open(path: T + 'static)
的 path
参数需要 'static
生命周期。
这是有道理的,因为它是在程序执行期间在 运行时间线程中处理的。我认为如果你能度过自己的一生会更有意义,因为 运行 时间不需要一直 运行 所以你可以扔掉一些东西。我是不是理解错了?
我现在想待 'static
,所以我的问题是……
我有一个 trait TraitN
和一些 struct StructX { path: String, }
有一个 fn new(path: &String) -> Box<TraitN>
。 new
创建并设置 self.path = path.to_string();
.
在 StructX
的一些实现 fn doit(&self) { ... }
中,我想调用 tokio::fs::File::open(&self.path)
.
如何在 'static
生命周期内通过 &self.path
?
这是一个完整的例子:
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...
您可以限制 &self
的生命周期:
impl StructX {
fn doit(&'static self) {
// here, we know that self and its members are 'static
}
}
如果你这样做,你实际上可能更好地让 StructX
首先存储路径的 'static
借用(而不是字符串)。
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(())
}
您不能引用
unwrap
的结果,因为没有任何东西拥有该值。你不能引用这种临时文件。克隆
&'a str
returns&'a str
,而不是String
.对值调用
wait
没有意义,因为它会阻塞线程。 运行 反应堆循环中的所有内容。
这个函数应该看起来像
fn start_runtime() -> Result<()> {
tokio::run({
get_path()
.map_err(|e| panic!("{}", e))
.and_then(|path| doit(path))
});
Ok(())
}
那么你所有的代码都应该切换到 impl Into<String>
而不是 &'static str
的 &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(())))
})
}
this [...] is config which doesn't change [...] read from a configfile during app init.
在这种情况下,create a singleton 将为您提供有效静态值:
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(())
}
您可以将其与 &'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.
你说对了一部分。今天的 Rust (1.30) 很难拥有最高性能的异步代码,因为 Rust 想要确保内存安全高于一切。这并不意味着代码性能不佳,只是还有改进的余地。
老实说,在这里克隆不太可能成为性能瓶颈,但很烦人。这就是 async
and await
syntax 的用武之地。这将使 futures 能够更轻松地以惯用的 Rust 方式使用引用。
because the runtime does not need to run the whole time [...] Do I understand something wrong?
但是,async
和 await
仍然可能无法帮助您,因为默认情况下 Tokio 将 运行 您的未来放在不同的线程上。这是它需要 'static
绑定的主要原因之一。这可以防止 Tokio 线程引用超出范围的本地堆栈,从而引入内存不安全。然而,这并不是 Tokio 独有的问题。
另请参阅:
- How can I pass a reference to a stack variable to a thread?
其他位
似乎 在此代码中对 wait
的每个 调用都是对期货的滥用。您可能希望重新阅读 the Tokio docs 以更好地理解您应该如何链接期货。如果有一个 wait
调用,它通常在所有内容的末尾,即使在使用 Tokio 时也很少见。
另请参阅:
我现在可以自己回答了:
fn make_static_str<T>(s: T) -> &'static str where T: Into<String>
Arc<Mutex<String>>
的解决方案 - 测试失败,因为在 playground 上没有可读取的文件。