Rust также знает об этих видах "совместимости" типов: все это о подтипах и дисперсии .Хитрость заключается в том, чтобы просто сделать так, чтобы оба аргумента имели один и тот же тип, по крайней мере, что касается функции .
Давайте сначала попробуем что-нибудь попроще:
// Both arguments have the same type (including the same lifetime)
fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 {
x
}
let outer = 3;
{
let inner = 27;
println!("{}", foo(&outer, &inner));
}
Почему это работает?outer
и inner
явно имеют разные времена жизни!Потому что при вызове функции Rust рассматривает подтип, чтобы посмотреть, можно ли вызвать функцию.В частности, тип &'outer i32
является подтипом &'inner i32
, поэтому его можно без проблем превратить в &'inner i32
и вызвать функцию.
Применение этой идеи к вашей проблеме означает, что ваша функция имеет только один тип функции и оба аргумента (base
и subs
) имеют этот тип:
fn selector<U, V, F: Fn(U) -> V>(base: F, subs: F, arg: U, use_base: bool) -> V {
match use_base {
true => base(arg),
false => subs(arg),
}
}
Еслимы пытаемся так, но, к сожалению, все равно получаем ошибку: " ожидаемый элемент fn, найден другой элемент fn ".Это связано с проблемой «элемент функции и указатель на функцию».Вы можете узнать больше об этом в этом ответе или в справочнике Rust .Приведение к указателю на функцию, к сожалению, не срабатывает в этой ситуации, поэтому одним из способов было бы явное приведение функций:
type FnStatic = fn(&str) -> &'static str;
type FnWeaker = fn(&str) -> &str;
selector(foo as FnStatic, baz as FnStatic, "local", false);
selector(bar as FnWeaker, baz as FnStatic, "local", false);
selector(foo as FnStatic, bar as FnWeaker, "local", false);
( Playground )
Это на самом деле работает, как и ожидалось: первые два вызова в порядке, но третьи ошибки:
error[E0308]: mismatched types
--> src/main.rs:7:31
|
7 | selector(foo as FnStatic, bar as FnWeaker, "local", false);
| ^^^^^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter
|
= note: expected type `for<'r> fn(&'r str) -> &str`
found type `for<'r> fn(&'r str) -> &'r str`
Однако все же немного уродливо приводить типы функций явно на сайте вызова.К сожалению, я не нашел способ это скрыть.Я попытался написать макрос, который принуждал к принуждению, но это не работало, когда указатель на функцию имел разные типы (включая второй пример).