Skip to content

Commit e9c80c7

Browse files
authored
Rollup merge of #113095 - WaffleLapkin:document_becoming_unuwuable, r=workingjubilee
Document `become` keyword The feature is not yet implemented, so I'm not sure if we should merge this _right away_, promoting an incomplete feature is probably not the best idea. But the docs can be reviewed while the implementation work is being done.
2 parents 637b50b + f13c8c2 commit e9c80c7

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

library/std/src/keyword_docs.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,6 +1257,108 @@ 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 prevents 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 unboundedly
1288+
/// [first, rest @ ..] => become fold(rest, f(init, *first), f),
1289+
/// [] => init,
1290+
/// }
1291+
/// }
1292+
/// ```
1293+
///
1294+
/// Compilers can already perform "tail call optimization" -- they can replace normal
1295+
/// calls with tail calls, although there are no guarantees that this will be done.
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. caller must not be marked with `#[track_caller]`
1327+
/// - callee is allowed to be marked with `#[track_caller]` as otherwise
1328+
/// adding `#[track_caller]` would be a breaking change. if callee is
1329+
/// marked with `#[track_caller]` a tail call is not guaranteed.
1330+
/// 4. callee and caller cannot be a closure
1331+
/// (unless it's coerced to a function pointer)
1332+
///
1333+
/// It is possible to tail-call a function pointer:
1334+
/// ```
1335+
/// #![feature(explicit_tail_calls)]
1336+
/// #![expect(incomplete_features)]
1337+
///
1338+
/// #[derive(Copy, Clone)]
1339+
/// enum Inst { Inc, Dec }
1340+
///
1341+
/// fn dispatch(stream: &[Inst], state: u32) -> u32 {
1342+
/// const TABLE: &[fn(&[Inst], u32) -> u32] = &[increment, decrement];
1343+
/// match stream {
1344+
/// [inst, rest @ ..] => become TABLE[*inst as usize](rest, state),
1345+
/// [] => state,
1346+
/// }
1347+
/// }
1348+
///
1349+
/// fn increment(stream: &[Inst], state: u32) -> u32 {
1350+
/// become dispatch(stream, state + 1)
1351+
/// }
1352+
///
1353+
/// fn decrement(stream: &[Inst], state: u32) -> u32 {
1354+
/// become dispatch(stream, state - 1)
1355+
/// }
1356+
///
1357+
/// let program = &[Inst::Inc, Inst::Inc, Inst::Dec, Inst::Inc];
1358+
/// assert_eq!(dispatch(program, 0), 2);
1359+
/// ```
1360+
mod become_keyword {}
1361+
12601362
#[doc(keyword = "self")]
12611363
//
12621364
/// The receiver of a method, or the current module.

0 commit comments

Comments
 (0)