[英]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...
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(())
}
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. 您无法引用此类临时文件。
Cloning a &'a str
returns a &'a str
, not a String
. 克隆&'a str
返回一个&'a str
,而不是String
。
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. 这就是async
和await
语法的来源。这将使期货更容易以惯用的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. 但是, async
和await
仍然可能无法帮助您,因为默认情况下,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: 也可以看看:
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: 我现在可以自己回答:
fn make_static_str<T>(s: T) -> &'static str where T: Into<String>
Arc<Mutex<String>>
- The test fails because there is no file to read on playground. 使用Arc<Mutex<String>>
解决方案-测试失败,因为在运动场上没有要读取的文件。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.