recursive/
lib.rs

1//! Rust has a problematic relationship with recursive functions, because functions that recurse
2//! deeply can overflow the stack, crashing your program. This crate makes it easy to remedy
3//! this problem by marking (indirectly) recursive functions as such:
4//! ```rust
5//! use recursive::recursive;
6//!
7//! #[recursive]
8//! fn sum(nums: &[u64]) -> u64 {
9//!     if let Some((head, tail)) = nums.split_first() {
10//!         head + sum(tail)
11//!     } else {
12//!         0
13//!     }
14//! }
15//! ```
16//!
17//! The way this prevents stack overflows is by checking the size of the remaining stack at the
18//! start of each call to your function. If this size is under a boundary set by
19//! [`set_minimum_stack_size`] (by default 128 KiB), a new stack is allocated and execution
20//! continues on that stack. This new stack's size is set using [`set_stack_allocation_size`], which
21//! is 2 MiB by default.
22//!
23//! This crate works by wrapping your function body in a call to [`stacker::maybe_grow`]. If this
24//! crate is not flexible enough for your needs consider using [`stacker`] directly yourself.
25//!
26//! ## What are the downsides?
27//!
28//! This crate is **not** zero cost, but it is also not limited to simple tail recursion or direct
29//! recursion. However, in most cases the stack size test is very fast and almost always succeeds
30//! without needing to allocate. If your recursive algorithm is very performance-sensitive I would
31//! suggest rewriting it to an iterative version regardless.
32//!
33//! This crate only supports those platforms that [`stacker`] supports. The Rust compiler itself
34//! uses [`stacker`], so the platform you're compiling on should always be fine, but for more
35//! obscure targets see its documentation.
36//!
37//! ## Which functions should I mark as `#[recursive]`?
38//!
39//! Any function that directly calls itself should be marked as `#[recursive]`, unless you know for
40//! certain that the stack is sufficiently large for any inputs that function will be called with.
41//! If you are feeding untrusted input into a recursive function you should always mark it as
42//! `#[recursive]`.
43//!
44//! It is not necessary to mark every single function that can indirectly recurse as `#[recursive]`.
45//! As long as every possible cycle of function calls includes at least one function marked
46//! `#[recursive]` you will be protected against stack overflows due to recursion.
47
48use std::sync::atomic::{AtomicUsize, Ordering};
49
50// These are referred to only by the procedural macro.
51#[doc(hidden)]
52pub mod __impl {
53    #[allow(unused)]
54    pub use stacker;
55}
56
57/// Marks a function to use an automatically growing segmented stack.
58///
59/// See the [crate docs](crate) for details.
60pub use recursive_proc_macro_impl::recursive;
61
62static MINIMUM_STACK_SIZE: AtomicUsize = AtomicUsize::new(128 * 1024);
63static STACK_ALLOC_SIZE: AtomicUsize = AtomicUsize::new(2 * 1024 * 1024);
64
65/// This sets the minimum stack size that [`recursive`] requires.
66///
67/// If a function tagged with `#[recursive]` is called when the remaining stack size is less than
68/// this value a new stack gets allocated.
69///
70/// The default value if this function is never called is 128 KiB.
71pub fn set_minimum_stack_size(bytes: usize) {
72    MINIMUM_STACK_SIZE.store(bytes, Ordering::Relaxed);
73}
74
75/// Returns the value set by [`set_minimum_stack_size`].
76pub fn get_minimum_stack_size() -> usize {
77    MINIMUM_STACK_SIZE.load(Ordering::Relaxed)
78}
79
80/// When a new stack gets allocated it will get allocated with this size.
81///
82/// The default value if this function is never called is 2 MiB.
83pub fn set_stack_allocation_size(bytes: usize) {
84    STACK_ALLOC_SIZE.store(bytes, Ordering::Relaxed);
85}
86
87/// Returns the value set by [`set_stack_allocation_size`].
88pub fn get_stack_allocation_size() -> usize {
89    STACK_ALLOC_SIZE.load(Ordering::Relaxed)
90}