Skip to content

Add the boxed!() macro to "de-magic" box syntax #3057

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

Closed
wants to merge 3 commits into from
Closed
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
114 changes: 114 additions & 0 deletions text/3057-boxed-macro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
- Feature Name: `boxed_macro`
- Start Date: 2020-12-29
- RFC PR: [rust-lang/rfcs#3057](https://github.com/rust-lang/rfcs/pull/3057)
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)

# Summary
[summary]: #summary

Introduces the `boxed` macro, which allows one to construct data directly on the heap.
This provides an alternative method to box syntax that does not rely on magic syntax. In addition,
if box syntax is significantly revised in the future, these changes could be applied to the `boxed` macro to
prevent ecosystem breakage.

# Motivation
[motivation]: #motivation

At the moment, the only safe way of constructing data directly on the heap in Rust is via box syntax.
The [`vec`](https://doc.rust-lang.org/src/alloc/macros.rs.html#37-47) macro is implemented in terms of it,
and several other parts of the [standard library](https://doc.rust-lang.org/src/alloc/sync.rs.html#314-318)
use it.
[`Box::new`](https://doc.rust-lang.org/std/boxed/struct.Box.html#method.new) is the stable alternative to this;
Copy link
Contributor

@KodrAus KodrAus Jan 12, 2021

Choose a reason for hiding this comment

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

Can we think of any real-world examples that aren't already served by the vec! macro that are likely to be big enough to overflow the stack?

This function for example:

fn big_boxed_slice(init: u8) -> Box<[u8]> {
    Box::new([init; 10485760])
}

allocates a 10MiB array, and (currently) overflows in both debug and release mode.

The alternative with box expr (or boxed!) would be:

fn big_boxed_syntax_slice(init: u8) -> Box<[u8]> {
    boxed!([init; 10485760])
}

which doesn't overflow, but is equivalent to the already stable:

fn big_vec(init: u8) -> Box<[u8]> {
    vec![init; 10485760].into_boxed_slice()
}

Playground link

however, in certain cases, it may not inline properly, which can cause the value to be instantiated on the
stack before being copied to the heap. This can blow up the stack.

However, box syntax has attracted criticism in its use of magic syntax. It adds an unneeded level
of complexity to the language for something as specific as instantiating a `Box`. The purpose of this macro is
to "hide" this complexity from stable compiler users by integrating it into a macro, which is already a standard
part of the Rust language.

A benefit of this is that, if box syntax is significantly changed or even removed in the future, the `boxed`
macro could be updated to reflect this change to prevent ecosystem breakage. For instance, if we decided to
change box syntax to be similar to the `await` syntax (e.g. `x.box`), we could update the `boxed` macro to
expand to `x.box` instead of `box x`. Then, ecosystem users of `boxed` would not be affected by this change
at all.

# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

The `boxed` macro would be used in almost exactly the same cases that box syntax would be used in. Consider
the following case, where data must be directly allocated on the heap.

```rust
/// The Hectapus is like an octopus, but with 100 arms.
#[derive(Debug)]
struct Hectapus {
arm_lengths: [u32; 100],
}

fn main() {
// We can expect our hectapus tracking software to run on the embedded
// computers hectapus scientists carry. Therefore, we need to initialize it
// on the heap.
let mut hectapus = box Hectapus { arm_lengths: [0; 100] };

for i in 0u32..100 {
hectapus.arm_lengths[i as usize] = i * 2;
}

println!("{:?}", &hectapus);
}
```

The following regex can be applied to replace instance of box syntax with the `boxed` macro:

```
s/box \(.*\)/boxed!(\1)/g
```

# Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

The `boxed` macro could be implemented in the standard library approximately as follows:

```rust
#[allow_internal_unstable(box_syntax)]
macro_rules! boxed {
($item: expr) => {{
box $item
}}
}
```

Of course, if box syntax is ever updated, this macro should be as well.

# Drawbacks
[drawbacks]: #drawbacks

Replacing instances of box syntax in the ecosystem with the `boxed` macro would likely be a source of
annoyance.

# Rationale and alternatives
[rationale-and-alternatives]: #rationale-and-alternatives

An alternative to this would be the simply stabilize box syntax, as this mostly serves as a way of
"pseudo-stablizing" the syntax so its technical benefits can be used in the broader ecosystem. However,
as this is likely years away, this macro serves as a way of bypassing box syntax.

# Prior art
[prior-art]: #prior-art

Replacing special syntax with a more generalized one has already been done before with the `raw_ref_macros`
feature in relation to `raw_ref_ops`. By replacing "magic" syntax with macro syntax, they were able to
stabilize their feature much earlier.

# Unresolved questions
[unresolved-questions]: #unresolved-questions

Should there be a lint to replace `box x` with `boxed!(x)`?

# Future possibilities
[future-possibilities]: #future-possibilities

If a way to initialize memory on the stack without magic is introduced to the Rust language, `boxed` could be
modified to support that.