I'm writing a mocking framework. To do so, I need to accept a function which can be used as a replacement of another function and store it. My current design does that by forcing the same input and output types, but it fails completely at forcing correct lifetimes.
Instead, I need to write a generic function, which accepts a base function and its substitute:
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),
}
}
By "substitute", I mean a function which accepts at least every argument accepted by base and for each of them returns value at least usable in every place, where value returned from base are. For example:
fn foo(_: &str) -> &'static str {
"foo"
}
fn bar(s: &str) -> &str {
s
}
fn baz(_: &str) -> &'static str {
"baz"
}
baz
is a substitute of foo
and bar
because its returned string can be used either instead of a 'static
one or be dependent on a borrow. bar
is not a substitute of foo
because a borrowed value can't be used in place of a 'static
one.
I want to create something like this, but it doesn't compile:
// FAILURE
// V
fn selector<U, V, F: Fn(U) -> V, G: F>(base: F, subs: G) {...}
The problem is that I can't express the relation between F
and G
. Rust does not seem to have a notion of supertype of concrete type.