#rust
#Ржавчина
Вопрос:
Следующий код (игровая площадка) не удается скомпилировать:
async fn wrapper<F, Fut>(func: F)
where
F: FnOnce(amp;i32) -> Fut,
Fut: Future<Output = ()>,
{
let i = 5;
func(amp;i).await;
}
async fn myfunc(_: amp;i32) {}
fn main() {
wrapper(myfunc);
}
Сообщение об ошибке:
error: implementation of `std::ops::FnOnce` is not general enough
--> src/main.rs:15:5
|
15 | wrapper(myfunc);
| ^^^^^^^ implementation of `std::ops::FnOnce` is not general enough
|
= note: `std::ops::FnOnce<(amp;'0 i32,)>` would have to be implemented for the type `for<'_> fn(amp;i32) -> impl std::future::Future {myfunc}`, for some specific lifetime `'0`...
= note: ...but `std::ops::FnOnce<(amp;i32,)>` is actually implemented for the type `for<'_> fn(amp;i32) -> impl std::future::Future {myfunc}`
Я нашел несколько похожих элементов, в которых упоминаются границы признаков с более высоким рейтингом (например, эта проблема с Rust и эта), и попытался поэкспериментировать с for<'a>
дополнительными ограничениями времени жизни, но ошибка мне все еще не ясна.
Я не понимаю, почему компилятор не может определить время жизни, тем более что более простая версия без фьючерсов компилируется просто отлично:
fn myfunc(_: amp;i32) {}
fn wrapper<F: FnOnce(amp;i32)>(func: F) {
let i = 5;
func(amp;i);
}
fn main() {
wrapper(myfunc);
}
В чем здесь проблема? Есть ли обходной путь, который может сделать эту компиляцию, например, с дополнительными ограничениями?
Ответ №1:
Исходя из того, что объясняется в предоставленной вами проблеме, для функций, ожидаемых вашей оболочкой, может быть создан определенный признак.
Будучи сам новичком в Rust, я точно не знаю, почему в вашей оригинальной версии компилятор жалуется на несоответствие типов, которые фактически эквивалентны в отображаемом сообщении об ошибке (это еще более очевидно, когда у нас были явные времена жизни; отображаемые типы идеально совпадают!). Я просто предполагаю, что это связано с тем фактом, что точный тип результата скрыт за impl
обозначением, а фактическая разница заключается в этой скрытой части.
Действительно, трудно выразить, что любое время жизни for<'r>
должно учитываться как для amp;
параметра, так и для Future
результата, потому что они появляются в двух разных ограничениях where
предложения. Насколько я понимаю (не так много …), учет явного времени жизни для всего признака делает for<'r>
обозначения согласованными как для amp;
параметра, так Future
и для результата, потому что все это находится в одном и том же ограничении where
предложения.
Поскольку мы имеем дело со временем жизни, я решил заменить копируемое i32
на не копируемое String
, чтобы предотвратить любые неожиданные упрощения.
Как указано в приведенной вами ссылке, это решение, похоже, работает только с функциями, а не с замыканиями.
use std::future::Future;
async fn wrapper<F>(func: F)
where
F: for<'r> Wrapped<'r>,
{
let s = String::from("WRAPPED");
func.call_once(amp;s).await;
}
trait Wrapped<'a> {
type Res: Future<Output = ()>;
fn call_once(
self,
s: amp;'a String,
) -> Self::Res;
}
impl<'a, F, FutRes> Wrapped<'a> for F
where
F: FnOnce(amp;'a String) -> FutRes,
FutRes: Future<Output = ()> 'a,
{
type Res = FutRes;
fn call_once(
self,
s: amp;'a String,
) -> Self::Res {
self(s)
}
}
async fn call_myfunc() {
let s = String::from("HARDCODED");
myfunc(amp;s).await;
}
async fn myfunc(arg: amp;String) {
println!("arg={}", arg);
}
fn main() {
println!("~~~~ hardcoded call ~~~~");
let f = call_myfunc();
futures::executor::block_on(f);
println!("~~~~ use wrapper() ~~~~");
let f = wrapper(myfunc);
futures::executor::block_on(f);
}