#[tailcall]Expand description
Transforms a function definition so that all recursive calls within the body are guaranteed to use a single stack frame (via tail recursion).
For simple free functions and inherent methods that only tail-call themselves directly, the
macro lowers the item into an inline loop. More complex cases keep the hidden
thunk-builder shape and run through tailcall::runtime::Thunk.
For methods, the optimized path aliases the receiver once, reuses the non-receiver arguments as mutable loop state, and rewrites each direct self tail call into “compute the next arguments, assign them, and continue”.
§Example
ⓘ
use tailcall::tailcall;
fn factorial(input: u64) -> u64 {
#[tailcall]
fn factorial_inner(accumulator: u64, input: u64) -> u64 {
if input > 0 {
tailcall::call! { factorial_inner(accumulator * input, input - 1) }
} else {
accumulator
}
}
factorial_inner(1, input)
}§Requirements
- Tail-call sites must be written with
tailcall::call!and left in tail form:tailcall::call!currently supports direct function paths and method syntax onself.
ⓘ
use tailcall::tailcall;
#[tailcall]
fn factorial(input: u64) -> u64 {
if input > 0 {
input * tailcall::call! { factorial(input - 1) }
// ^^^^^^^ This is not allowed.
} else {
1
}
}ⓘ
use tailcall::tailcall;
#[tailcall]
fn factorial(input: u64) -> u64 {
if input > 0 {
1 + tailcall::call! { factorial(input - 1) }
// ^^^^^^^^^^^^^^^ This is not allowed.
} else {
1
}
}- Function arguments must use simple identifier patterns:
ⓘ
use tailcall::tailcall;
#[tailcall]
fn sum((left, right): (u64, u64)) -> u64 {
// ^^^^^^^^^^^^^ Simple identifier arguments are required.
left + right
}- Methods in
implblocks are supported, but trait methods are not:
ⓘ
use tailcall::tailcall;
trait Factorialable {
fn factorial(self) -> Self {
self.calc_factorial(1)
}
fn calc_factorial(self, accumulator: u64) -> u64;
}
impl Factorialable for u64 {
#[tailcall]
fn calc_factorial(self, accumulator: u64) -> u64 {
// ^^^^ Trait methods are not supported yet.
if self > 0 {
(self - 1).calc_factorial(self * accumulator)
} else {
accumulator
}
}
}async fnandconst fnare not supported:
ⓘ
use tailcall::tailcall;
#[tailcall]
async fn countdown(input: u64) -> u64 {
if input > 0 {
tailcall::call! { countdown(input - 1) }
} else {
0
}
}ⓘ
use tailcall::tailcall;
#[tailcall]
const fn countdown(input: u64) -> u64 {
if input > 0 {
tailcall::call! { countdown(input - 1) }
} else {
0
}
}