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);
}
答案 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(())
}
您无法引用unwrap
的结果,因为没有人拥有该值。您无法引用此类临时文件。
克隆&'a str
会返回&'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>
的{{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)
我现在可以自己回答:
fn make_static_str<T>(s: T) -> &'static str where T: Into<String>
Arc<Mutex<String>>
的解决方案-测试失败,因为在操场上没有可读取的文件。