我正在写一个模拟框架。要做到这一点,我必须接受它可以用作替换另一个函数的函数和存储。我目前的设计确实是迫使相同的输入和输出类型,但它迫使正确的寿命完全失败。
相反,我需要写一个通用的函数,它接受一个基础功能和它的替代品:
fn main() {
selector(foo, baz, "local", false);
selector(bar, baz, "local", false);
selector(foo, bar, "local", false); // SHOULD FAIL, bar is NOT substitute of foo
}
fn foo(_: &str) -> &'static str {
"foo"
}
fn bar(s: &str) -> &str {
s
}
fn baz(_: &str) -> &'static str {
"baz"
}
// DOES NOT COMPILE
// fn selector<U, V, F: Fn(U) -> V, G: F>(base: F, subs: G, arg: U, use_base: bool) -> V {
// match use_base {
// true => base(arg),
// false => subs(arg),
// }
// }
// COMPILES, but is too weak
fn selector<U, V, F: Fn(U) -> V, G: Fn(U) -> V>(base: F, subs: G, arg: U, use_base: bool) -> V {
match use_base {
true => base(arg),
false => subs(arg),
}
}
通过“替代”,我的意思是它接受至少由碱和用于它们中的每返回值中的每一个地方,在那里从基地返回的值是至少使用所接受每个参数的功能。例如:
fn foo(_: &str) -> &'static str {
"foo"
}
fn bar(s: &str) -> &str {
s
}
fn baz(_: &str) -> &'static str {
"baz"
}
baz
是foo
和bar
的替代品,因为其返回的字符串不仅可以被用来代替'static
一个或依赖于借位。 bar
不是foo
的替代品,因为借值不能代替'static
一个一起使用。
我想创造这样的事情,但它不会编译:
// FAILURE
// V
fn selector<U, V, F: Fn(U) -> V, G: F>(base: F, subs: G) {...}
问题是,我无法表达F
和G
之间的关系。锈似乎不具有具体类型的超类型的概念。
拉斯特也知道这些种类型“兼容性”:这是所有关于subtyping and variance。诀窍是只是让这两个参数具有相同类型的,至少就功能而言。
让我们尝试一些轻松先:
// 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
显然有不同的寿命!由于调用函数时,拉斯特认为子类型,看是否该函数可以调用。特别地,该类型&'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项目”。这同“功能项目对函数指针”的问题做。您可以了解更多有关in this answer或in the Rust reference。为一个函数指针的强制是可惜不是踢在这种情况下,这样一个方法是显式转换功能:
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);
(Qazxswpoi)
这实际上按预期工作:前两个电话都很好,但第三个错误搭配:
Playground
然而,它仍然是一个有点难看,在调用点显式转换函数的类型。不幸的是,我还没有找到一种方法来掩饰这一点。我试着写,强迫威逼宏,但这并没有对当函数指针有不同的类型(包括第二个例子)工作。