Basic example, with pure functions:
fn main ()
{
type Int = u32;
fn is_even (n: Int) -> bool
{
trace_fn! { "is_even({})", n =>
n == 0 || is_odd(n - 1) // FUNCTION BODY
}
}
fn is_odd (n: Int) -> bool
{
trace_fn! { "is_odd({})", n =>
n != 0 && is_even(n - 1) // FUNCTION BODY
}
}
dbg!(is_even(5));
}
prints
is_even(5)?
| is_odd(4)?
| | is_even(3)?
| | | is_odd(2)?
| | | | is_even(1)?
| | | | | is_odd(0)?
| | | | | +-> false
| | | | +-> false
| | | +-> false
| | +-> false
| +-> false
+-> false
[src/main.rs:47] is_even(5) = false
Regarding an example with closures, this, as @OptimisticPeach said, won't be possible in general, due to it requiring two "objects" that refer to each other, thus implying self-referentiability which, as we know, is a problematic pattern in Rust.
However, this can be avoided by using static
closures (it requires naming the type of a closure, which in stable can only be done with environment-less closures by coercing them to fn()
pointers), since a static
is not part of a closure's environment:
#[allow(non_upper_case_globals)]
fn main ()
{
type Int = u32;
static is_even: fn(Int) -> bool = |n| {
trace_fn! { "is_even({})", n =>
n == 0 || is_odd(n - 1)
}
};
static is_odd: fn(Int) -> bool = |n| {
trace_fn! { "is_odd({})", n =>
n != 0 && is_even(n - 1)
}
};
dbg!(is_even(14));
}
Lastly, and this one is the most interesting, is that since a closure is just an object with a method, we can avoid the self-referentiability issue by having one "object" with two methods (we lose some sugar but it remains fine).
(In my example, is_even
and is_odd
will be sharing a reference to an out_stream
:
type Int = u32;
/// Our "double closure" trait
trait EvenTheOdds {
fn is_even (&mut self, n: Int) -> bool;
fn is_odd (&mut self, n: Int) -> bool;
}
struct TraceFn<W : Write> {
out_stream: W,
}
impl<W : Write> EvenTheOdds for TraceFn<W> {
fn is_even (&mut self, n: Int) -> bool
{
trace_fn! { using &mut self.out_stream, "is_even({})", n =>
n == 0 || self.is_odd(n - 1)
}
}
fn is_odd (&mut self, n: Int) -> bool
{
trace_fn! { using &mut self.out_stream, "is_odd({})", n =>
n != 0 && self.is_even(n - 1)
}
}
}
fn main ()
{
let mut stderr_tracer = TraceFn { out_stream: io::stderr() };
dbg!(stderr_tracer.is_even(14));
}