Ответ 1
Есть несколько способов сделать это.
Вы можете поместить замыкания в структуру и передать эту структуру замыканию. Вы даже можете определить структуры, встроенные в функцию:
fn main() {
struct Fact<'s> { f: & Fn(&Fact, u32) -> u32 }
let fact = Fact {
f: &|fact, x| if x == 0 {1} else {x * (fact.f)(fact, x - 1)}
};
println!("{}", (fact.f)(&fact, 5));
}
Это позволяет обойти проблему наличия бесконечного типа (функция, которая принимает себя в качестве аргумента) и проблему, заключающуюся в том, что fact
еще не определен внутри самого замыкания, когда кто-то пишет let fact = |x| {...}
let fact = |x| {...}
и поэтому там нельзя ссылаться на это.
Это работает в Rust 1.17, но, возможно, в будущем станет незаконным, поскольку в некоторых случаях это опасно, как об этом говорится в сообщении в блоге "Случай повторяющегося закрытия". Здесь совершенно безопасно, поскольку мутации нет.
Другой вариант - просто написать рекурсивную функцию как элемент fn
, который также может быть определен внутри функции:
fn main() {
fn fact(x: u32) -> u32 { if x == 0 {1} else {x * fact(x - 1)} }
println!("{}", fact(5));
}
Это прекрасно работает, если вам не нужно захватывать что-либо из окружающей среды.
Еще один вариант - использовать решение fn
item, но явно передать нужные аргументы args/environment.
fn main() {
struct FactEnv { base_case: u32 }
fn fact(env: &FactEnv, x: u32) -> u32 {
if x == 0 {env.base_case} else {x * fact(env, x - 1)}
}
let env = FactEnv { base_case: 1 };
println!("{}", fact(&env, 5));
}
Все они работают с Rust 1.17 и, вероятно, работали с версии 0.6. Значения fn
определенные внутри fn
, не отличаются от тех, которые определены на верхнем уровне, за исключением того, что они доступны только в пределах fn
они определены.