Indeed, early versions of Rust (before 2013) did have a “GC” (garbage collection) feature. Why was this feature removed later? This can be traced back to the syntax that was removed from Rust.
Managed and Owned Boxes
In early versions of Rust, there were two main pointer types: Managed Box (Managed Box) and Owned Box (Owned Box).
Managed Box
Managed Box is denoted by the @T syntax:
- Memory is managed by the garbage collector.
- Allows multiple references to point to the same data.
- Suitable for cyclic references.
Managed Boxrefers to values allocated on the heap.
// Here, x is just a pointer on the heap
let x = @10;
// A string allocated on the heap
let y = @"Hello World";
Owned Box
Owned Box is denoted by the ~T syntax:
- It is based on the ownership system and does not require garbage collection.
- Memory is automatically released when the value goes out of scope.
- There can only be one owner at a time.
// This x is also a pointer allocated on the heap
let x = ~10;
// A string allocated on the heap
let y = ~"Hello World";
During an important phase of Rust’s language evolution, there were attempts to support both garbage collection and manual memory management. These attempts reflect Rust’s exploration process in memory management.
Why can’t we see the above syntax anymore?
Until 2013, the Rust team was looking for ways to simplify Rust’s memory management story. They primarily reviewed Rust’s memory management with the idea of removing garbage collection from the core language and moving it to the standard library, using a minimal set of language hooks to implement flexible, pluggable automatic memory management.
One of the most common questions asked by almost every Rust beginner in the early days was:
“When should I use managed pointers, and when should I use owned pointers?”
Or, more simply,
“What are all these
~and@symbols?”
They identified several reasons for the confusion. The main ones are:
- For programmers accustomed to languages like Java, which do not distinguish between stack and heap, understanding the difference between stack and heap is difficult. This is a fundamental challenge when working with systems languages. Without taking control of allocation away from the programmer, it is hard to address this issue. However, doing so would undermine the language’s goals, as precise control over whether allocations occur on the stack or heap is crucial in low-level, performance-critical programming.
- The
~and@symbols made the code unfamiliar before learning certain concepts in the Rust language. Unlike other punctuation marks in Rust, they are not part of the standard C-like punctuation, making the language intimidating to users. While keywords have the benefit of being self-documenting, punctuation marks lack this advantage. Therefore, switching to keywords could help. However, the author acknowledges that the aesthetics of syntax vary from person to person, and if the community prefers the current syntax, he would accept not changing it. - Having two heaps in Rust confused beginners about where to allocate memory. This situation arose because concurrent systems followed the principle of “minimizing sharing by default.” However, over the years, concurrent systems have been part of libraries rather than the language itself, making the presence of two heaps seem inappropriate.
- Programmers were unsure whether to use
~or@in certain operations, as different operations could use either symbol. For a long time, it was unclear which would dominate, leading to prolonged debates about which to introduce first. As the language and community evolved and coding standards stabilized, the owned pointer~became the clear winner. The actual rule was that programmers should prioritize using~in all cases, only using@when they couldn’t determine when to release the associated object.
Clearly, ~ has taken the lead over @. @ was once thought to be widely used and could simplify the learning curve for those unaccustomed to destructor-based memory management and references. However, in practice, libraries used owned pointers (~), so Rust programmers had to quickly learn ~. Once they learned to use ~ effectively, @ was relegated to a marginal role. ~ has many advantages, such as deterministic allocation and destruction, good interaction with the standard library, freedom from GC marking pauses, simpler semantics, attachability in vector and string operations, and the ability to send across tasks.
As beginners become familiar with this approach and explore libraries, they will learn more dynamic memory management methods, such as using GC types for garbage collection, Rc types for reference counting, and Arc types for thread-safe reference counting. By building only ~ into the language, it becomes more opinionated, reducing confusion.
An interesting application scenario for Rust is very low-level programming, even down to the kernel level. Languages like Java, Ruby, and JavaScript manage memory automatically and dynamically at runtime, trading control and performance for convenience. Most of the remaining software is written in C and C++, requiring manual memory management to achieve a combination of performance, simplicity, and/or self-hosting capabilities. If Rust provided a simple, fully automatic memory management system and only exposed safe manual memory management mechanisms when needed for maximum performance, or if it could further drive the development of the emerging Rust community.
Changes
Based on the above considerations, the decision was made to remove the @ pointer syntax for Managed Box. Instead, Rc (reference counting) and Arc (atomic reference counting) are used to handle situations requiring shared ownership. Meanwhile, the ~ syntax was deprecated, and its functionality was taken over by smart pointer and collection types like Box, Vec, and String, which provide more explicit and safe ownership semantics.
Summary
Perhaps it is precisely the removal of the @ syntax with the garbage collector that makes Rust unique, allowing it to stand out in the programming world with its no-GC, memory-safe features.