@@ -1257,6 +1257,105 @@ mod ref_keyword {}
1257
1257
/// [`async`]: ../std/keyword.async.html
1258
1258
mod return_keyword { }
1259
1259
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
+
1260
1359
#[ doc( keyword = "self" ) ]
1261
1360
//
1262
1361
/// The receiver of a method, or the current module.
0 commit comments