Skip to content

Document become keyword #113095

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions library/std/src/keyword_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,105 @@ mod ref_keyword {}
/// [`async`]: ../std/keyword.async.html
mod return_keyword {}

#[doc(keyword = "become")]
//
/// Perform a tail-call of a function.
///
/// <div class="warning">
///
/// `feature(explicit_tail_calls)` is currently incomplete and may not work properly.
/// </div>
///
/// When tail calling a function, instead of its stack frame being added to the
/// stack, the stack frame of the caller is directly replaced with the callee's.
/// This means that as long as a loop in a call graph only uses tail calls, the
/// stack growth will be bounded.
///
/// This is useful for writing functional-style code (since it prevent recursion
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// This is useful for writing functional-style code (since it prevent recursion
/// This is useful for writing functional-style code (since it prevents recursion

/// from exhausting resources) or for code optimization (since a tail call
/// *might* be cheaper than a normal call, tail calls can be used in a similar
/// manner to computed goto).
///
/// Example of using `become` to implement functional-style `fold`:
/// ```
/// #![feature(explicit_tail_calls)]
/// #![expect(incomplete_features)]
///
/// fn fold<T: Copy, S>(slice: &[T], init: S, f: impl Fn(S, T) -> S) -> S {
/// match slice {
/// // without `become`, on big inputs this could easily overflow the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// // without `become`, on big inputs this could easily overflow the
/// // without `become`, on large inputs this could easily overflow the

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no actual rule for it, but I will try to explain this: both "big" and "large" are pretty the same in casual speech, but in techical/scientific writings "large" is more preffered term, it just sounds more neutral or formal

(https://langeek.co/en/grammar/course/1100/big-vs-large)

/// // stack. using a tail call guarantees that the stack will not grow unboundly
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// // stack. using a tail call guarantees that the stack will not grow unboundly
/// // stack. using a tail call guarantees that the stack will not grow unboundedly

/// [first, rest @ ..] => become fold(rest, f(init, *first), f),
/// [] => init,
/// }
/// }
/// ```
///
/// Compiler can already perform "tail call optimization" -- it can replace
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Compiler can already perform "tail call optimization" -- it can replace
/// The compiler can already perform "tail call optimization" -- it can replace

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I agree with this, can you elaborate?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"compiler" is singular and countable, I believe such nouns require a determiner

but if we drop the formalities, I just feel that way and sentence does looks more complete with "the"

/// normal calls with tail calls (although no guarantees if it will perform it).
/// However, to perform TCO, the call needs to be the last thing that happens
/// in the functions and be returned from it. This requirement is often broken
/// by drop code for locals, which is run after computing the return expression:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// by drop code for locals, which is run after computing the return expression:
/// by drop code for locals, which runs after computing the return expression:

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is run seems fine to me?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, looking at this now, this is fine as it is

///
/// ```
/// fn example() {
/// let string = "meow".to_owned();
/// println!("{string}");
/// return help(); // this is *not* the last thing that happens in `example`...
/// }
///
/// // ... because it is desugared to this:
/// fn example_desugared() {
/// let string = "meow".to_owned();
/// println!("{string}");
/// let tmp = help();
/// drop(string);
/// return tmp;
/// }
///
/// fn help() {}
/// ```
///
/// For this reason `become` also changes the drop order, such that locals are
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// For this reason `become` also changes the drop order, such that locals are
/// For this reason, `become` also changes the drop order, such that locals are

/// dropped *before* evaluating the call.
///
/// In order to guarantee that the compiler can perform a tail call, `become`
/// currently has these requirements:
/// 1. callee and caller must have the same ABI, arguments and return type
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// 1. callee and caller must have the same ABI, arguments and return type
/// 1. callee and caller must have the same ABI, arguments, and return type

I believe it is Serial comma case here

/// 2. callee and caller must not have varargs
/// 3. callee and caller must not be marked with `#[track_caller]`
/// 4. callee and caller cannot be a closure
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// 4. callee and caller cannot be a closure
/// 4. callee and caller cannot be a closures

Shouldn’t it be plural, since varargs above are plural? Not sure about this honestly

/// (unless it's coerced to a function pointer)
///
/// It is possible to tail-call a function pointer:
/// ```
/// #![feature(explicit_tail_calls)]
/// #![expect(incomplete_features)]
///
/// #[derive(Copy, Clone)]
/// enum Inst { Inc, Dec }
///
/// fn dispatch(stream: &[Inst], state: u32) -> u32 {
/// const TABLE: &[fn(&[Inst], u32) -> u32] = &[increment, decrement];
/// match stream {
/// [inst, rest @ ..] => become TABLE[*inst as usize](rest, state),
/// [] => state,
/// }
/// }
///
/// fn increment(stream: &[Inst], state: u32) -> u32 {
/// become dispatch(stream, state + 1)
/// }
///
/// fn decrement(stream: &[Inst], state: u32) -> u32 {
/// become dispatch(stream, state - 1)
/// }
///
/// let program = &[Inst::Inc, Inst::Inc, Inst::Dec, Inst::Inc];
/// assert_eq!(dispatch(program, 0), 2);
/// ```
mod become_keyword {}

#[doc(keyword = "self")]
//
/// The receiver of a method, or the current module.
Expand Down
Loading