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
возвращает &'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>
вместо &str
из&'static 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(())))
})
}
это [...] конфигурация, которая не изменяет [...] чтение из файла конфигурацииво время инициализации приложения.
В этом случае создайте синглтон , который даст вам фактически статическое значение:
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(())
}
Вы можете связать это с ответом phimuemue , чтобы получить &'static MyConfigStruct
, который затем может иметь fn foo(&'static self)
, что доступно.
Должно быть что-то не так с языком, если это становится настолько сложным и требует многократного запоминания.
Вы частично правы.Трудно иметь максимально производительный асинхронный код с сегодняшним Rust (1.30), потому что Rust хочет обеспечить безопасность памяти превыше всего.Это не означает, что код не работает, просто есть немного места для улучшения.
Честно говоря, создание клонов здесь вряд ли будет узким местом для производительности, но это раздражает.Вот тут и вступают async
и await
синтаксис . Это позволит фьючерсам более легко использовать ссылки в идиоматическом стиле Rust.
, поскольку среда выполнения ненужно все время работать [...] Я понимаю, что что-то не так?
Однако async
и await
все еще могут не помочь вам, так как по умолчанию Токио будет работать на васдругая тема.Это одна из основных причин, по которой требуется ограничение 'static
.Это препятствует тому, чтобы поток Tokio имел ссылку на локальный стек, выходящий из области видимости, создавая небезопасную память.Однако это не единственная проблема для Tokio.
См. Также:
Другие биты
Похоже, что каждый отдельный вызов wait
в этом коде является неправильным использованием фьючерсов.Возможно, вы захотите перечитать Документы Tokio , чтобы лучше понять, как вы должны связывать будущее.Если есть вызов wait
, он обычно в конце всего, и даже это редко при использовании Tokio.
См. Также: