Ich versuche eine Funktion zu schreiben, die zwei Funktionen zusammensetzt. Das anfängliche Design ist ziemlich einfach: Eine Funktion, die zwei Funktionen übernimmt und eine zusammengesetzte Funktion zurückgibt, die ich dann mit anderen Funktionen zusammenstellen kann, da Rust keine Ruheparameter hat. Ich bin auf eine Wand gestoßen, die mit frustrierenden, nicht hilfreichen Compilerfehlern gebaut wurde.
Meine Kompositionsfunktion:
fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box<Fn(A) -> C + 'a>
where
F: 'a + Fn(A) -> B + Sized,
G: 'a + Fn(B) -> C + Sized,
{
Box::new(move |x| g(f(x)))
}
Wie ich es benutzen möchte:
fn main() {
let addAndMultiply = compose(|x| x * 2, |x| x + 2);
let divideAndSubtract = compose(|x| x / 2, |x| x - 2);
let finally = compose(*addAndMultiply, *divideAndSubtract);
println!("Result is {}", finally(10));
}
Dem Compiler gefällt das nicht, egal was ich versuche, die Merkmalsgrenzen werden niemals erfüllt. Der Fehler ist:
error[E0277]: the size for values of type `dyn std::ops::Fn(_) -> _` cannot be known at compilation time
--> src/main.rs:13:19
|
13 | let finally = compose(*addAndMultiply, *divideAndSubtract);
| ^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `dyn std::ops::Fn(_) -> _`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
note: required by `compose`
--> src/main.rs:1:1
|
1 | / fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box<Fn(A) -> C + 'a>
2 | | where
3 | | F: 'a + Fn(A) -> B + Sized,
4 | | G: 'a + Fn(B) -> C + Sized,
5 | | {
6 | | Box::new(move |x| g(f(x)))
7 | | }
| |_^
functional-programming
rust
Seun LanLege
quelle
quelle
Antworten:
Wie @ljedrz hervorhebt , müssen Sie nur noch einmal auf die zusammengesetzten Funktionen verweisen , damit es funktioniert:
let finally = compose(&*multiply_and_add, &*divide_and_subtract);
(Beachten Sie, dass in Rust die Konvention vorschreibt, dass Variablennamen in snake_case stehen sollen.)
Wir können dies jedoch verbessern!
Seit Rust 1.26 können wir abstrakte Rückgabetypen verwenden (zuvor als gated gekennzeichnet
#![feature(conservative_impl_trait)]
). Dies kann Ihnen dabei helfen, Ihr Beispiel erheblich zu vereinfachen, da Sie die Lebensdauern, Referenzen,Sized
Einschränkungen undBox
es überspringen können :fn compose<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C where F: Fn(A) -> B, G: Fn(B) -> C, { move |x| g(f(x)) } fn main() { let multiply_and_add = compose(|x| x * 2, |x| x + 2); let divide_and_subtract = compose(|x| x / 2, |x| x - 2); let finally = compose(multiply_and_add, divide_and_subtract); println!("Result is {}", finally(10)); }
Schließlich, da Sie Ruheparameter erwähnen, vermute ich, dass Sie tatsächlich die Möglichkeit haben möchten, so viele Funktionen, wie Sie möchten, auf flexible Weise zu verketten. Ich habe dieses Makro zu diesem Zweck geschrieben:
macro_rules! compose { ( $last:expr ) => { $last }; ( $head:expr, $($tail:expr), +) => { compose_two($head, compose!($($tail),+)) }; } fn compose_two<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C where F: Fn(A) -> B, G: Fn(B) -> C, { move |x| g(f(x)) } fn main() { let add = |x| x + 2; let multiply = |x| x * 2; let divide = |x| x / 2; let intermediate = compose!(add, multiply, divide); let subtract = |x| x - 2; let finally = compose!(intermediate, subtract); println!("Result is {}", finally(10)); }
quelle
impl Trait
Argumentationsposition verwenden, um die Dinge ein wenig weiter zu vereinfachen.compose_two
ist nicht unbedingt notwendig. Das Inlinen der Funktion innerhalb des Makros funktioniert, kann aber schreckliche Kompilierungsfehler verursachen, wenn die Typen nicht übereinstimmen:( $head:expr, $($tail:expr), +) => { |x| compose!($($tail),+)($head(x)) }
Fügen Sie einfach Referenzen hinzu
finally
und es wird funktionieren:fn main() { let addAndMultiply = compose(|x| x * 2, |x| x + 2); let divideAndSubtract = compose(|x| x / 2, |x| x - 2); let finally = compose(&*addAndMultiply, &*divideAndSubtract); println!("Result is {}", finally(10)); }
Dereferenzieren
addAndMultiply
oderdivideAndSubtract
Aufdecken eines Merkmalsobjekts, das es nicht istSized
; Es muss entweder in a eingeschlossenBox
oder referenziert werden, damit es an eine Funktion mit einerSized
Einschränkung übergeben werden kann.quelle