Skip to content

Commit 46ffda7

Browse files
committed
Document become keyword
1 parent 4b55fe1 commit 46ffda7

File tree

1 file changed

+99
-0
lines changed

1 file changed

+99
-0
lines changed

library/std/src/keyword_docs.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,6 +1257,105 @@ mod ref_keyword {}
12571257
/// [`async`]: ../std/keyword.async.html
12581258
mod return_keyword {}
12591259

1260+
#[doc(keyword = "become")]
1261+
//
1262+
/// Perform a tail-call of a function.
1263+
///
1264+
/// <div class="warning">
1265+
///
1266+
/// `feature(explicit_tail_calls)` is currently incomplete and may not work properly.
1267+
/// </div>
1268+
///
1269+
/// When tail calling a function, instead of its stack frame being added to the
1270+
/// stack, the stack frame of the caller is directly replaced with the callee's.
1271+
/// This means that as long as a loop in a call graph only uses tail calls, the
1272+
/// stack growth will be bounded.
1273+
///
1274+
/// This is useful for writing functional-style code (since it prevent recursion
1275+
/// from exhausting resources) or for code optimization (since a tail call
1276+
/// *might* be cheaper than a normal call, tail calls can be used in a similar
1277+
/// manner to computed goto).
1278+
///
1279+
/// Example of using `become` to implement functional-style `fold`:
1280+
/// ```
1281+
/// #![feature(explicit_tail_calls)]
1282+
/// #![expect(incomplete_features)]
1283+
///
1284+
/// fn fold<T: Copy, S>(slice: &[T], init: S, f: impl Fn(S, T) -> S) -> S {
1285+
/// match slice {
1286+
/// // without `become`, on big inputs this could easily overflow the
1287+
/// // stack. using a tail call guarantees that the stack will not grow unboundly
1288+
/// [first, rest @ ..] => become fold(rest, f(init, *first), f),
1289+
/// [] => init,
1290+
/// }
1291+
/// }
1292+
/// ```
1293+
///
1294+
/// Compiler can already perform "tail call optimization" -- it can replace
1295+
/// normal calls with tail calls (although no guarantees if it will perform it).
1296+
/// However, to perform TCO, the call needs to be the last thing that happens
1297+
/// in the functions and be returned from it. This requirement is often broken
1298+
/// by drop code for locals, which is run after computing the return expression:
1299+
///
1300+
/// ```
1301+
/// fn example() {
1302+
/// let string = "meow".to_owned();
1303+
/// println!("{string}");
1304+
/// return help(); // this is *not* the last thing that happens in `example`...
1305+
/// }
1306+
///
1307+
/// // ... because it is desugared to this:
1308+
/// fn example_desugared() {
1309+
/// let string = "meow".to_owned();
1310+
/// println!("{string}");
1311+
/// let tmp = help();
1312+
/// drop(string);
1313+
/// return tmp;
1314+
/// }
1315+
///
1316+
/// fn help() {}
1317+
/// ```
1318+
///
1319+
/// For this reason `become` also changes the drop order, such that locals are
1320+
/// dropped *before* evaluating the call.
1321+
///
1322+
/// In order to guarantee that the compiler can perform a tail call, `become`
1323+
/// currently has these requirements:
1324+
/// 1. callee and caller must have the same ABI, arguments and return type
1325+
/// 2. callee and caller must not have varargs
1326+
/// 3. callee and caller must not be marked with `#[track_caller]`
1327+
/// 4. callee and caller cannot be a closure
1328+
/// (unless it's coerced to a function pointer)
1329+
///
1330+
/// It is possible to tail-call a function pointer:
1331+
/// ```
1332+
/// #![feature(explicit_tail_calls)]
1333+
/// #![expect(incomplete_features)]
1334+
///
1335+
/// #[derive(Copy, Clone)]
1336+
/// enum Inst { Inc, Dec }
1337+
///
1338+
/// fn dispatch(stream: &[Inst], state: u32) -> u32 {
1339+
/// const TABLE: &[fn(&[Inst], u32) -> u32] = &[increment, decrement];
1340+
/// match stream {
1341+
/// [inst, rest @ ..] => become TABLE[*inst as usize](rest, state),
1342+
/// [] => state,
1343+
/// }
1344+
/// }
1345+
///
1346+
/// fn increment(stream: &[Inst], state: u32) -> u32 {
1347+
/// become dispatch(stream, state + 1)
1348+
/// }
1349+
///
1350+
/// fn decrement(stream: &[Inst], state: u32) -> u32 {
1351+
/// become dispatch(stream, state - 1)
1352+
/// }
1353+
///
1354+
/// let program = &[Inst::Inc, Inst::Inc, Inst::Dec, Inst::Inc];
1355+
/// assert_eq!(dispatch(program, 0), 2);
1356+
/// ```
1357+
mod become_keyword {}
1358+
12601359
#[doc(keyword = "self")]
12611360
//
12621361
/// The receiver of a method, or the current module.

0 commit comments

Comments
 (0)