@@ -1257,6 +1257,108 @@ mod ref_keyword {}
12571257/// [`async`]: ../std/keyword.async.html
12581258mod 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