Skip to content

Commit f951ce6

Browse files
committed
de-RFC: Remove unsized_locals
1 parent d51997c commit f951ce6

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed

text/3829-de-rfc-unsized-locals.md

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
- Feature Name: `unsized_locals`
2+
- Start Date: 2025-06-02
3+
- RFC PR: [rust-lang/rfcs#3829](https://github.com/rust-lang/rfcs/pull/3829)
4+
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)
5+
6+
7+
_Following the great success of the [type ascription de-RFC], we now present the next one in the series!_
8+
9+
# Summary
10+
[summary]: #summary
11+
12+
Unsized locals ([RFC 1909], called "unsized rvalues" originally)
13+
has been a merged RFC for eight years with no clear path to stabilization.
14+
15+
There is still a very large gap in the implementation in rustc that hasn't been adressed for many years and there are several language problems with this feature.
16+
17+
This RFC intends to advocate for the feature being removed entirely with a fresh RFC being necessary to add a similiar feature again.
18+
19+
# Demotivation
20+
[demotivation]: #demotivation
21+
22+
The `unsized_locals` feature is simple to explain on the surface: local variables no longer have to be `Sized`.
23+
24+
```rust
25+
#![feature(unsized_locals)]
26+
fn main() {
27+
let x = *Box::from("hello, world!");
28+
}
29+
```
30+
31+
This will dynamically allocate space on the stack for the string.
32+
C has a similiar feature, [`alloca`] and variable length arrays (VLA), the latter having been made optional to implement in C11.
33+
34+
## Lack of Proper Implementation
35+
36+
This feature has never been properly implemented in rustc.
37+
The variable length array form proposed in the RFC still doesn't exist at all, only the unsized local variable does.
38+
It is implemented in the type checker and codegen, but lacking MIR semantics and therefore unimplemented in the compile time function evaluator.
39+
This is very significant, as MIR semantics govern how the feature should behave precisely in the first place.
40+
Without them, they cannot work in `const` and optimizations are likely broken around them.
41+
Because of this lack of implementation quality, the `unsized_locals` feature was already accepted for removal from rustc two years ago in [MCP 630].
42+
This removal hasn't yet been implemented.
43+
44+
## Implicit Danger
45+
46+
While lack of implementation quality is a sign of lack of interest for the feature, it is not the primary reason for this RFC,
47+
which is purely about the langauge design of the feature.
48+
49+
The original RFC was very short, and especially short on motivation and rationale for the design.
50+
51+
Dynamic stack allocation in geneneral has a rather significant downside: it makes it easy to accidentally overflow the stack if you allocate a lot.
52+
Stacks are usually rather small (on the order of few megabytes, depending on the platform), which means that dynamically allocating user-controlled input on the stack is often rather dangerous.
53+
While stack overflows are not considered memory unsafe by Rust, they still cause crashes which can lead to denial of service vulnerabilities and unrealiableness in general.
54+
Dynamic stack allocation as currently implemented interacts especially poorly with loops.
55+
Allocations are not freed until the function returns, so the following example overflows the stack:
56+
57+
```rust
58+
#![feature(unsized_locals)]
59+
fn main() {
60+
let s = "A".repeat(1000);
61+
for i in 0..1000000 {
62+
let x: str = *Box::from(s.as_str());
63+
std::hint::black_box(&s);
64+
}
65+
}
66+
```
67+
There are ways around this (rewinding the stack pointer at the end of the loop to free up the memory), but they are not currently implemented.
68+
69+
Dynamic stack allocation also has its upsides.
70+
It is generally faster than heap allocation and can therefore improve performance in cases where the previously mentioned downsides are not a concern.
71+
Therefore, this RFC is not necessarily a rejection of the idea of dynamic stack allocation, but merely the way the `unsized_locals` feature exposes it.
72+
73+
Which is where we get to the major argument of this RFC: The `unsized_locals` feature integrates far too naturally into Rust.
74+
This makes the feature very **easy to use**, and especially **easy to use accidentally**.
75+
76+
As previously mentioned, dynamic stack allocation can be dangerous and should not be used lightly.
77+
It's an advanced optimization feature that is best left untouched by default.
78+
As such, it behaves similarly to `unsafe` (but is not actually `unsafe`).
79+
With `unsized_locals`, the use of dynamic stack allocation is completely implicit.
80+
When you create an unsized local, it is often not obvious that dynamic stack allocation is happening.
81+
In the example from the start, we need to be aware of all the involved types (which are often inferred in practice) to know that this is a potentially problematic unsized local that we have to audit more carefully instead of a normal sized local.
82+
Especially around strings, which are often user-controlled, this easily lead to accidental dangerous situations.
83+
84+
Rusts strings, reference types, and `Sized` are a part of the language that can often be hard to understand for beginners coming from garbage collected languages.
85+
By allowing people to create a dynamic stack allocation without being aware of what is happening, we open the doors for people, especially new Rust programmers who are not intimately familiar with the tradeoffs of dynamic stack allocation, to shoot themselves in the foot and become vulnerable to unexpected crashes.
86+
87+
As a _dangerous_ feature, dynamic stack allocation must be explicit and obvious - and `unsized_locals` makes it implicit _by design_.
88+
Therefore, `unsized_locals` must go.
89+
90+
# Guide-level obfuscation
91+
[guide-level-obfuscation]: #guide-level-obfuscation
92+
93+
The `unsized_locals` feature is removed from the compiler and [RFC 1909] is officially unaccepted.
94+
95+
If someone wants to bring dynamic stack allocation into Rust again, a new design will have to be designed from scratch, considering all the problems laid out in this RFC.
96+
97+
# Drawforwards
98+
[drawforwards]: #drawforwards
99+
100+
This feature has the previously mentioned performance upsides that users could profit from if it was stabilized.
101+
But this benefit applies to other ways to expose dynamic stack allocation too, and other ways are likely to be easier to implement correctly and stabilize.
102+
103+
# Irrationale and alternatives
104+
[irrationale-and-alternatives]: #irrationale-and-alternatives
105+
106+
If nothing is done on the language side, the feature will likely still be removed from the compiler.
107+
This puts the feature into a really bad position, but it may be readded in the future if someone desires.
108+
With this RFC, the fate of `unsized_locals` is sealed and it becomes clear to anyone what the state of the feature is.
109+
110+
The intent of this RFC is not to proposed an alternative way to solve dynamic stack allocation,
111+
but there are some listed alternatives here that may be considered in the future if desired.
112+
While this RFC doesn't explicitly encourage people to revisit this topic, it may result in new activity around dynamic stack allocation in Rust.
113+
114+
[RFC 1808] proposed an `alloca` function, which was rejected because `alloca` does not really behave like a function.
115+
116+
[RFC 1808] then changed to propose the the VLA syntax instead.
117+
It was rejected in favor of more general unsized values, which culminated in [RFC 1909].
118+
119+
The [`alloca` crate](https://crates.io/crates/alloca) implements dynamic stack allocation via a closure indirection and FFI with C.
120+
121+
The `unsized_fn_params` feature doesn't suffer from the same problems as `unsized_locals` and will still be kept around.
122+
It is independent of this RFC.
123+
124+
# Posterior art
125+
[posterior-art]: #posterior-
126+
127+
The best prior art for this removal is of course the inspiration for the de-RFC format, the [type-ascription de-RFC].
128+
[MCP 630] can also be seen as prior art
129+
130+
# Unresolved answers
131+
[unresolved-answers]: #unresolved-answers
132+
133+
None
134+
135+
# Future probabilities
136+
[future-probabilities]: #future-probabilities
137+
138+
In the future, dynamic stack allocation may be re-added to Rust via some other feature that solves the explicitness problems outlined in the motivation.
139+
140+
Alternatively, it could be decided, explicitly or implicitly through inaction, that dynamic stack allocation is not a fit for Rust and will not be added.
141+
142+
[type-ascription de-RFC]: https://rust-lang.github.io/rfcs/3307-de-rfc-type-ascription.html
143+
[`alloca`]: https://man7.org/linux/man-pages/man3/alloca.3.html
144+
[MCP 630]: https://github.com/rust-lang/compiler-team/issues/630
145+
[RFC 1808]: https://github.com/rust-lang/rfcs/pull/1808
146+
[RFC 1909]: https://rust-lang.github.io/rfcs/1909-unsized-rvalues.html

0 commit comments

Comments
 (0)