Auto merge of #25320 - steveklabnik:rollup, r=steveklabnik
- Successful merges: #25254, #25272, #25278, #25282, #25283, #25288, #25292, #25302, #25304, #25314 - Failed merges:
This commit is contained in:
commit
a90453a178
@ -31,23 +31,27 @@ You may also be interested in the [grammar].
|
||||
|
||||
## Unicode productions
|
||||
|
||||
A few productions in Rust's grammar permit Unicode code points outside the ASCII
|
||||
range. We define these productions in terms of character properties specified
|
||||
in the Unicode standard, rather than in terms of ASCII-range code points. The
|
||||
section [Special Unicode Productions](#special-unicode-productions) lists these
|
||||
productions.
|
||||
A few productions in Rust's grammar permit Unicode code points outside the
|
||||
ASCII range. We define these productions in terms of character properties
|
||||
specified in the Unicode standard, rather than in terms of ASCII-range code
|
||||
points. The grammar has a [Special Unicode Productions][unicodeproductions]
|
||||
section that lists these productions.
|
||||
|
||||
[unicodeproductions]: grammar.html#special-unicode-productions
|
||||
|
||||
## String table productions
|
||||
|
||||
Some rules in the grammar — notably [unary
|
||||
operators](#unary-operator-expressions), [binary
|
||||
operators](#binary-operator-expressions), and [keywords](#keywords) — are
|
||||
operators](#binary-operator-expressions), and [keywords][keywords] — are
|
||||
given in a simplified form: as a listing of a table of unquoted, printable
|
||||
whitespace-separated strings. These cases form a subset of the rules regarding
|
||||
the [token](#tokens) rule, and are assumed to be the result of a
|
||||
lexical-analysis phase feeding the parser, driven by a DFA, operating over the
|
||||
disjunction of all such string table entries.
|
||||
|
||||
[keywords]: grammar.html#keywords
|
||||
|
||||
When such a string enclosed in double-quotes (`"`) occurs inside the grammar,
|
||||
it is an implicit reference to a single member of such a string table
|
||||
production. See [tokens](#tokens) for more information.
|
||||
@ -75,7 +79,7 @@ An identifier is any nonempty Unicode[^non_ascii_idents] string of the following
|
||||
- The first character has property `XID_start`
|
||||
- The remaining characters have property `XID_continue`
|
||||
|
||||
that does _not_ occur in the set of [keywords](#keywords).
|
||||
that does _not_ occur in the set of [keywords][keywords].
|
||||
|
||||
> **Note**: `XID_start` and `XID_continue` as character properties cover the
|
||||
> character ranges used to form the more familiar C and Java language-family
|
||||
@ -401,7 +405,7 @@ Symbols are a general class of printable [token](#tokens) that play structural
|
||||
roles in a variety of grammar productions. They are catalogued here for
|
||||
completeness as the set of remaining miscellaneous printable tokens that do not
|
||||
otherwise appear as [unary operators](#unary-operator-expressions), [binary
|
||||
operators](#binary-operator-expressions), or [keywords](#keywords).
|
||||
operators](#binary-operator-expressions), or [keywords][keywords].
|
||||
|
||||
|
||||
## Paths
|
||||
@ -547,7 +551,7 @@ _name_ s that occur in its body. At the "current layer", they all must repeat
|
||||
the same number of times, so ` ( $( $i:ident ),* ; $( $j:ident ),* ) => ( $(
|
||||
($i,$j) ),* )` is valid if given the argument `(a,b,c ; d,e,f)`, but not
|
||||
`(a,b,c ; d,e)`. The repetition walks through the choices at that layer in
|
||||
lockstep, so the former input transcribes to `( (a,d), (b,e), (c,f) )`.
|
||||
lockstep, so the former input transcribes to `(a,d), (b,e), (c,f)`.
|
||||
|
||||
Nested repetitions are allowed.
|
||||
|
||||
@ -611,7 +615,7 @@ module needs its own source file: [module definitions](#modules) can be nested
|
||||
within one file.
|
||||
|
||||
Each source file contains a sequence of zero or more `item` definitions, and
|
||||
may optionally begin with any number of [attributes](#Items and attributes)
|
||||
may optionally begin with any number of [attributes](#items-and-attributes)
|
||||
that apply to the containing module, most of which influence the behavior of
|
||||
the compiler. The anonymous crate module can have additional attributes that
|
||||
apply to the crate as a whole.
|
||||
@ -653,7 +657,7 @@ There are several kinds of item:
|
||||
* [`use` declarations](#use-declarations)
|
||||
* [modules](#modules)
|
||||
* [functions](#functions)
|
||||
* [type aliases](#type-aliases)
|
||||
* [type definitions](grammar.html#type-definitions)
|
||||
* [structures](#structures)
|
||||
* [enumerations](#enumerations)
|
||||
* [constant items](#constant-items)
|
||||
@ -773,7 +777,7 @@ extern crate std as ruststd; // linking to 'std' under another name
|
||||
A _use declaration_ creates one or more local name bindings synonymous with
|
||||
some other [path](#paths). Usually a `use` declaration is used to shorten the
|
||||
path required to refer to a module item. These declarations may appear at the
|
||||
top of [modules](#modules) and [blocks](#blocks).
|
||||
top of [modules](#modules) and [blocks](grammar.html#block-expressions).
|
||||
|
||||
> **Note**: Unlike in many languages,
|
||||
> `use` declarations in Rust do *not* declare linkage dependency with external crates.
|
||||
@ -1144,9 +1148,7 @@ let px: i32 = match p { Point(x, _) => x };
|
||||
```
|
||||
|
||||
A _unit-like struct_ is a structure without any fields, defined by leaving off
|
||||
the list of fields entirely. Such types will have a single value, just like
|
||||
the [unit value `()`](#unit-and-boolean-literals) of the unit type. For
|
||||
example:
|
||||
the list of fields entirely. Such types will have a single value. For example:
|
||||
|
||||
```
|
||||
struct Cookie;
|
||||
@ -2436,11 +2438,6 @@ comma:
|
||||
(0); // zero in parentheses
|
||||
```
|
||||
|
||||
### Unit expressions
|
||||
|
||||
The expression `()` denotes the _unit value_, the only value of the type with
|
||||
the same name.
|
||||
|
||||
### Structure expressions
|
||||
|
||||
There are several forms of structure expressions. A _structure expression_
|
||||
@ -3281,7 +3278,7 @@ constructor or `struct` field may refer, directly or indirectly, to the
|
||||
enclosing `enum` or `struct` type itself. Such recursion has restrictions:
|
||||
|
||||
* Recursive types must include a nominal type in the recursion
|
||||
(not mere [type definitions](#type-definitions),
|
||||
(not mere [type definitions](grammar.html#type-definitions),
|
||||
or other structural types such as [arrays](#array,-and-slice-types) or [tuples](#tuple-types)).
|
||||
* A recursive `enum` item must have at least one non-recursive constructor
|
||||
(in order to give the recursion a basis case).
|
||||
|
@ -176,7 +176,7 @@ for a full example, the core of which is reproduced here:
|
||||
|
||||
```ignore
|
||||
declare_lint!(TEST_LINT, Warn,
|
||||
"Warn about items named 'lintme'")
|
||||
"Warn about items named 'lintme'");
|
||||
|
||||
struct Pass;
|
||||
|
||||
|
@ -127,12 +127,12 @@ fn grow(&self) -> Circle {
|
||||
We just say we’re returning a `Circle`. With this method, we can grow a new
|
||||
circle to any arbitrary size.
|
||||
|
||||
# Static methods
|
||||
# Associated functions
|
||||
|
||||
You can also define static methods that do not take a `self` parameter. Here’s a
|
||||
pattern that’s very common in Rust code:
|
||||
You can also define associated functions that do not take a `self` parameter.
|
||||
Here’s a pattern that’s very common in Rust code:
|
||||
|
||||
```
|
||||
```rust
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
|
@ -1,3 +1,570 @@
|
||||
% The Stack and the Heap
|
||||
|
||||
Coming Soon
|
||||
As a systems language, Rust operates at a low level. If you’re coming from a
|
||||
high-level language, there are some aspects of systems programming that you may
|
||||
not be familiar with. The most important one is how memory works, with a stack
|
||||
and a heap. If you’re familiar with how C-like languages use stack allocation,
|
||||
this chapter will be a refresher. If you’re not, you’ll learn about this more
|
||||
general concept, but with a Rust-y focus.
|
||||
|
||||
# Memory management
|
||||
|
||||
These two terms are about memory management. The stack and the heap are
|
||||
abstractions that help you determine when to allocate and deallocate memory.
|
||||
|
||||
Here’s a high-level comparison:
|
||||
|
||||
The stack is very fast, and is where memory is allocated in Rust by default.
|
||||
But the allocation is local to a function call, and is limited in size. The
|
||||
heap, on the other hand, is slower, and is explicitly allocated by your
|
||||
program. But it’s effectively unlimited in size, and is globally accessible.
|
||||
|
||||
# The Stack
|
||||
|
||||
Let’s talk about this Rust program:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x = 42;
|
||||
}
|
||||
```
|
||||
|
||||
This program has one variable binding, `x`. This memory needs to be allocated
|
||||
from somewhere. Rust ‘stack allocates’ by default, which means that basic
|
||||
values ‘go on the stack’. What does that mean?
|
||||
|
||||
Well, when a function gets called, some memory gets allocated for all of its
|
||||
local variables and some other information. This is called a ‘stack frame’, and
|
||||
for the purpose of this tutorial, we’re going to ignore the extra information
|
||||
and just consider the local variables we’re allocating. So in this case, when
|
||||
`main()` is run, we’ll allocate a single 32-bit integer for our stack frame.
|
||||
This is automatically handled for you, as you can see, we didn’t have to write
|
||||
any special Rust code or anything.
|
||||
|
||||
When the function is over, its stack frame gets deallocated. This happens
|
||||
automatically, we didn’t have to do anything special here.
|
||||
|
||||
That’s all there is for this simple program. The key thing to understand here
|
||||
is that stack allocation is very, very fast. Since we know all the local
|
||||
variables we have ahead of time, we can grab the memory all at once. And since
|
||||
we’ll throw them all away at the same time as well, we can get rid of it very
|
||||
fast too.
|
||||
|
||||
The downside is that we can’t keep values around if we need them for longer
|
||||
than a single function. We also haven’t talked about what that name, ‘stack’
|
||||
means. To do that, we need a slightly more complicated example:
|
||||
|
||||
```rust
|
||||
fn foo() {
|
||||
let y = 5;
|
||||
let z = 100;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = 42;
|
||||
|
||||
foo();
|
||||
}
|
||||
```
|
||||
|
||||
This program has three variables total: two in `foo()`, one in `main()`. Just
|
||||
as before, when `main()` is called, a single integer is allocated for its stack
|
||||
frame. But before we can show what happens when `foo()` is called, we need to
|
||||
visualize what’s going on with memory. Your operating system presents a view of
|
||||
memory to your program that’s pretty simple: a huge list of addresses, from 0
|
||||
to a large number, representing how much RAM your computer has. For example, if
|
||||
you have a gigabyte of RAM, your addresses go from `0` to `1,073,741,824`. That
|
||||
number comes from 2<sup>30</sup>, the number of bytes in a gigabyte.
|
||||
|
||||
This memory is kind of like a giant array: addresses start at zero and go
|
||||
up to the final number. So here’s a diagram of our first stack frame:
|
||||
|
||||
| Address | Name | Value |
|
||||
+---------+------+-------+
|
||||
| 0 | x | 42 |
|
||||
|
||||
We’ve got `x` located at address `0`, with the value `42`.
|
||||
|
||||
When `foo()` is called, a new stack frame is allocated:
|
||||
|
||||
| Address | Name | Value |
|
||||
+---------+------+-------+
|
||||
| 2 | z | 100 |
|
||||
| 1 | y | 5 |
|
||||
| 0 | x | 42 |
|
||||
|
||||
Because `0` was taken by the first frame, `1` and `2` are used for `foo()`’s
|
||||
stack frame. It grows upward, the more functions we call.
|
||||
|
||||
|
||||
There’s some important things we have to take note of here. The numbers 0, 1,
|
||||
and 2 are all solely for illustrative purposes, and bear no relationship to the
|
||||
actual numbers the computer will actually use. In particular, the series of
|
||||
addresses are in reality going to be separated by some number of bytes that
|
||||
separate each address, and that separation may even exceed the size of the
|
||||
value being stored.
|
||||
|
||||
After `foo()` is over, its frame is deallocated:
|
||||
|
||||
| Address | Name | Value |
|
||||
+---------+------+-------+
|
||||
| 0 | x | 42 |
|
||||
|
||||
And then, after `main()`, even this last value goes away. Easy!
|
||||
|
||||
It’s called a ‘stack’ because it works like a stack of dinner plates: the first
|
||||
plate you put down is the last plate to pick back up. Stacks are sometimes
|
||||
called ‘last in, first out queues’ for this reason, as the last value you put
|
||||
on the stack is the first one you retrieve from it.
|
||||
|
||||
Let’s try a three-deep example:
|
||||
|
||||
```rust
|
||||
fn bar() {
|
||||
let i = 6;
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
let a = 5;
|
||||
let b = 100;
|
||||
let c = 1;
|
||||
|
||||
bar();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = 42;
|
||||
|
||||
foo();
|
||||
}
|
||||
```
|
||||
|
||||
Okay, first, we call `main()`:
|
||||
|
||||
| Address | Name | Value |
|
||||
+---------+------+-------+
|
||||
| 0 | x | 42 |
|
||||
|
||||
Next up, `main()` calls `foo()`:
|
||||
|
||||
| Address | Name | Value |
|
||||
+---------+------+-------+
|
||||
| 3 | c | 1 |
|
||||
| 2 | b | 100 |
|
||||
| 1 | a | 5 |
|
||||
| 0 | x | 42 |
|
||||
|
||||
And then `foo()` calls `bar()`:
|
||||
|
||||
| Address | Name | Value |
|
||||
+---------+------+-------+
|
||||
| 4 | i | 6 |
|
||||
| 3 | c | 1 |
|
||||
| 2 | b | 100 |
|
||||
| 1 | a | 5 |
|
||||
| 0 | x | 42 |
|
||||
|
||||
Whew! Our stack is growing tall.
|
||||
|
||||
After `bar()` is over, its frame is deallocated, leaving just `foo()` and
|
||||
`main()`:
|
||||
|
||||
| Address | Name | Value |
|
||||
+---------+------+-------+
|
||||
| 3 | c | 1 |
|
||||
| 2 | b | 100 |
|
||||
| 1 | a | 5 |
|
||||
| 0 | x | 42 |
|
||||
|
||||
And then `foo()` ends, leaving just `main()`
|
||||
|
||||
| Address | Name | Value |
|
||||
+---------+------+-------+
|
||||
| 0 | x | 42 |
|
||||
|
||||
And then we’re done. Getting the hang of it? It’s like piling up dishes: you
|
||||
add to the top, you take away from the top.
|
||||
|
||||
# The Heap
|
||||
|
||||
Now, this works pretty well, but not everything can work like this. Sometimes,
|
||||
you need to pass some memory between different functions, or keep it alive for
|
||||
longer than a single function’s execution. For this, we can use the heap.
|
||||
|
||||
In Rust, you can allocate memory on the heap with the [`Box<T>` type][box].
|
||||
Here’s an example:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x = Box::new(5);
|
||||
let y = 42;
|
||||
}
|
||||
```
|
||||
|
||||
[box]: ../std/boxed/index.html
|
||||
|
||||
Here’s what happens in memory when `main()` is called:
|
||||
|
||||
| Address | Name | Value |
|
||||
+---------+------+--------+
|
||||
| 1 | y | 42 |
|
||||
| 0 | x | ?????? |
|
||||
|
||||
We allocate space for two variables on the stack. `y` is `42`, as it always has
|
||||
been, but what about `x`? Well, `x` is a `Box<i32>`, and boxes allocate memory
|
||||
on the heap. The actual value of the box is a structure which has a pointer to
|
||||
‘the heap’. When we start executing the function, and `Box::new()` is called,
|
||||
it allocates some memory for the heap, and puts `5` there. The memory now looks
|
||||
like this:
|
||||
|
||||
| Address | Name | Value |
|
||||
+-----------------+------+----------------+
|
||||
| 2<sup>30</sup> | | 5 |
|
||||
| ... | ... | ... |
|
||||
| 1 | y | 42 |
|
||||
| 0 | x | 2<sup>30</sup> |
|
||||
|
||||
We have 2<sup>30</sup> in our hypothetical computer with 1GB of RAM. And since
|
||||
our stack grows from zero, the easiest place to allocate memory is from the
|
||||
other end. So our first value is at the highest place in memory. And the value
|
||||
of the struct at `x` has a [raw pointer][rawpointer] to the place we’ve
|
||||
allocated on the heap, so the value of `x` is 2<sup>30</sup>, the memory
|
||||
location we’ve asked for.
|
||||
|
||||
[rawpointer]: raw-pointers.html
|
||||
|
||||
We haven’t really talked too much about what it actually means to allocate and
|
||||
deallocate memory in these contexts. Getting into very deep detail is out of
|
||||
the scope of this tutorial, but what’s important to point out here is that
|
||||
the heap isn’t just a stack that grows from the opposite end. We’ll have an
|
||||
example of this later in the book, but because the heap can be allocated and
|
||||
freed in any order, it can end up with ‘holes’. Here’s a diagram of the memory
|
||||
layout of a program which has been running for a while now:
|
||||
|
||||
|
||||
| Address | Name | Value |
|
||||
+----------------------+------+----------------------+
|
||||
| 2<sup>30</sup> | | 5 |
|
||||
| (2<sup>30</sup>) - 1 | | |
|
||||
| (2<sup>30</sup>) - 2 | | |
|
||||
| (2<sup>30</sup>) - 3 | | 42 |
|
||||
| ... | ... | ... |
|
||||
| 3 | y | (2<sup>30</sup>) - 3 |
|
||||
| 2 | y | 42 |
|
||||
| 1 | y | 42 |
|
||||
| 0 | x | 2<sup>30</sup> |
|
||||
|
||||
In this case, we’ve allocated four things on the heap, but deallocated two of
|
||||
them. There’s a gap between 2<sup>30</sup> and (2<sup>30</sup>) - 3 which isn’t
|
||||
currently being used. The specific details of how and why this happens depends
|
||||
on what kind of strategy you use to manage the heap. Different programs can use
|
||||
different ‘memory allocators’, which are libraries that manage this for you.
|
||||
Rust programs use [jemalloc][jemalloc] for this purpose.
|
||||
|
||||
[jemalloc]: http://www.canonware.com/jemalloc/
|
||||
|
||||
Anyway, back to our example. Since this memory is on the heap, it can stay
|
||||
alive longer than the function which allocates the box. In this case, however,
|
||||
it doesn’t.[^moving] When the function is over, we need to free the stack frame
|
||||
for `main()`. `Box<T>`, though, has a trick up its sleve: [Drop][drop]. The
|
||||
implementation of `Drop` for `Box` deallocates the memory that was allocated
|
||||
when it was created. Great! So when `x` goes away, it first frees the memory
|
||||
allocated on the heap:
|
||||
|
||||
| Address | Name | Value |
|
||||
+---------+------+--------+
|
||||
| 1 | y | 42 |
|
||||
| 0 | x | ?????? |
|
||||
|
||||
[drop]: drop.html
|
||||
[moving]: We can make the memory live longer by transferring ownership,
|
||||
sometimes called ‘moving out of the box’. More complex examples will
|
||||
be covered later.
|
||||
|
||||
|
||||
And then the stack frame goes away, freeing all of our memory.
|
||||
|
||||
# Arguments and borrowing
|
||||
|
||||
We’ve got some basic examples with the stack and the heap going, but what about
|
||||
function arguments and borrowing? Here’s a small Rust program:
|
||||
|
||||
```rust
|
||||
fn foo(i: &i32) {
|
||||
let z = 42;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = &x;
|
||||
|
||||
foo(y);
|
||||
}
|
||||
```
|
||||
|
||||
When we enter `main()`, memory looks like this:
|
||||
|
||||
| Address | Name | Value |
|
||||
+---------+------+-------+
|
||||
| 1 | y | 0 |
|
||||
| 0 | x | 5 |
|
||||
|
||||
`x` is a plain old `5`, and `y` is a reference to `x`. So its value is the
|
||||
memory location that `x` lives at, which in this case is `0`.
|
||||
|
||||
What about when we call `foo()`, passing `y` as an argument?
|
||||
|
||||
| Address | Name | Value |
|
||||
+---------+------+-------+
|
||||
| 3 | z | 42 |
|
||||
| 2 | i | 0 |
|
||||
| 1 | y | 0 |
|
||||
| 0 | x | 5 |
|
||||
|
||||
Stack frames aren’t just for local bindings, they’re for arguments too. So in
|
||||
this case, we need to have both `i`, our argument, and `z`, our local variable
|
||||
binding. `i` is a copy of the argument, `y`. Since `y`’s value is `0`, so is
|
||||
`i`’s.
|
||||
|
||||
This is one reason why borrowing a variable doesn’t deallocate any memory: the
|
||||
value of a reference is just a pointer to a memory location. If we got rid of
|
||||
the underlying memory, things wouldn’t work very well.
|
||||
|
||||
# A complex example
|
||||
|
||||
Okay, let’s go through this complex program step-by-step:
|
||||
|
||||
```rust
|
||||
fn foo(x: &i32) {
|
||||
let y = 10;
|
||||
let z = &y;
|
||||
|
||||
baz(z);
|
||||
bar(x, z);
|
||||
}
|
||||
|
||||
fn bar(a: &i32, b: &i32) {
|
||||
let c = 5;
|
||||
let d = Box::new(5);
|
||||
let e = &d;
|
||||
|
||||
baz(e);
|
||||
}
|
||||
|
||||
fn baz(f: &i32) {
|
||||
let g = 100;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let h = 3;
|
||||
let i = Box::new(20);
|
||||
let j = &h;
|
||||
|
||||
foo(j);
|
||||
}
|
||||
```
|
||||
|
||||
First, we call `main()`:
|
||||
|
||||
| Address | Name | Value |
|
||||
+-----------------+------+----------------+
|
||||
| 2<sup>30</sup> | | 20 |
|
||||
| ... | ... | ... |
|
||||
| 2 | j | 0 |
|
||||
| 1 | i | 2<sup>30</sup> |
|
||||
| 0 | h | 3 |
|
||||
|
||||
We allocate memory for `j`, `i`, and `h`. `i` is on the heap, and so has a
|
||||
value pointing there.
|
||||
|
||||
Next, at the end of `main()`, `foo()` gets called:
|
||||
|
||||
| Address | Name | Value |
|
||||
+-----------------+------+----------------+
|
||||
| 2<sup>30</sup> | | 20 |
|
||||
| ... | ... | ... |
|
||||
| 5 | z | 4 |
|
||||
| 4 | y | 10 |
|
||||
| 3 | x | 0 |
|
||||
| 2 | j | 0 |
|
||||
| 1 | i | 2<sup>30</sup> |
|
||||
| 0 | h | 3 |
|
||||
|
||||
Space gets allocated for `x`, `y`, and `z`. The argument `x` has the same value
|
||||
as `j`, since that’s what we passed it in. It’s a pointer to the `0` address,
|
||||
since `j` points at `h`.
|
||||
|
||||
Next, `foo()` calls `baz()`, passing `z`:
|
||||
|
||||
| Address | Name | Value |
|
||||
+-----------------+------+----------------+
|
||||
| 2<sup>30</sup> | | 20 |
|
||||
| ... | ... | ... |
|
||||
| 7 | g | 100 |
|
||||
| 6 | f | 4 |
|
||||
| 5 | z | 4 |
|
||||
| 4 | y | 10 |
|
||||
| 3 | x | 0 |
|
||||
| 2 | j | 0 |
|
||||
| 1 | i | 2<sup>30</sup> |
|
||||
| 0 | h | 3 |
|
||||
|
||||
We’ve allocated memory for `f` and `g`. `baz()` is very short, so when it’s
|
||||
over, we get rid of its stack frame:
|
||||
|
||||
| Address | Name | Value |
|
||||
+-----------------+------+----------------+
|
||||
| 2<sup>30</sup> | | 20 |
|
||||
| ... | ... | ... |
|
||||
| 5 | z | 4 |
|
||||
| 4 | y | 10 |
|
||||
| 3 | x | 0 |
|
||||
| 2 | j | 0 |
|
||||
| 1 | i | 2<sup>30</sup> |
|
||||
| 0 | h | 3 |
|
||||
|
||||
Next, `foo()` calls `bar()` with `x` and `z`:
|
||||
|
||||
| Address | Name | Value |
|
||||
+----------------------+------+----------------------+
|
||||
| 2<sup>30</sup> | | 20 |
|
||||
| (2<sup>30</sup>) - 1 | | 5 |
|
||||
| ... | ... | ... |
|
||||
| 10 | e | 4 |
|
||||
| 9 | d | (2<sup>30</sup>) - 1 |
|
||||
| 8 | c | 5 |
|
||||
| 7 | b | 4 |
|
||||
| 6 | a | 0 |
|
||||
| 5 | z | 4 |
|
||||
| 4 | y | 10 |
|
||||
| 3 | x | 0 |
|
||||
| 2 | j | 0 |
|
||||
| 1 | i | 2<sup>30</sup> |
|
||||
| 0 | h | 3 |
|
||||
|
||||
We end up allocating another value on the heap, and so we have to subtract one
|
||||
from 2<sup>30</sup>. It’s easier to just write that than `1,073,741,823`. In any
|
||||
case, we set up the variables as usual.
|
||||
|
||||
At the end of `bar()`, it calls `baz()`:
|
||||
|
||||
| Address | Name | Value |
|
||||
+----------------------+------+----------------------+
|
||||
| 2<sup>30</sup> | | 20 |
|
||||
| (2<sup>30</sup>) - 1 | | 5 |
|
||||
| ... | ... | ... |
|
||||
| 12 | g | 100 |
|
||||
| 11 | f | 4 |
|
||||
| 10 | e | 4 |
|
||||
| 9 | d | (2<sup>30</sup>) - 1 |
|
||||
| 8 | c | 5 |
|
||||
| 7 | b | 4 |
|
||||
| 6 | a | 0 |
|
||||
| 5 | z | 4 |
|
||||
| 4 | y | 10 |
|
||||
| 3 | x | 0 |
|
||||
| 2 | j | 0 |
|
||||
| 1 | i | 2<sup>30</sup> |
|
||||
| 0 | h | 3 |
|
||||
|
||||
With this, we’re at our deepest point! Whew! Congrats for following along this
|
||||
far.
|
||||
|
||||
After `baz()` is over, we get rid of `f` and `g`:
|
||||
|
||||
| Address | Name | Value |
|
||||
+----------------------+------+----------------------+
|
||||
| 2<sup>30</sup> | | 20 |
|
||||
| (2<sup>30</sup>) - 1 | | 5 |
|
||||
| ... | ... | ... |
|
||||
| 10 | e | 4 |
|
||||
| 9 | d | (2<sup>30</sup>) - 1 |
|
||||
| 8 | c | 5 |
|
||||
| 7 | b | 4 |
|
||||
| 6 | a | 0 |
|
||||
| 5 | z | 4 |
|
||||
| 4 | y | 10 |
|
||||
| 3 | x | 0 |
|
||||
| 2 | j | 0 |
|
||||
| 1 | i | 2<sup>30</sup> |
|
||||
| 0 | h | 3 |
|
||||
|
||||
Next, we return from `bar()`. `d` in this case is a `Box<T>`, so it also frees
|
||||
what it points to: (2<sup>30</sup>) - 1.
|
||||
|
||||
| Address | Name | Value |
|
||||
+-----------------+------+----------------+
|
||||
| 2<sup>30</sup> | | 20 |
|
||||
| ... | ... | ... |
|
||||
| 5 | z | 4 |
|
||||
| 4 | y | 10 |
|
||||
| 3 | x | 0 |
|
||||
| 2 | j | 0 |
|
||||
| 1 | i | 2<sup>30</sup> |
|
||||
| 0 | h | 3 |
|
||||
|
||||
And after that, `foo()` returns:
|
||||
|
||||
| Address | Name | Value |
|
||||
+-----------------+------+----------------+
|
||||
| 2<sup>30</sup> | | 20 |
|
||||
| ... | ... | ... |
|
||||
| 2 | j | 0 |
|
||||
| 1 | i | 2<sup>30</sup> |
|
||||
| 0 | h | 3 |
|
||||
|
||||
And then, finally, `main()`, which cleans the rest up. When `i` is `Drop`ped,
|
||||
it will clean up the last of the heap too.
|
||||
|
||||
# What do other languages do?
|
||||
|
||||
Most languages with a garbage collector heap-allocate by default. This means
|
||||
that every value is boxed. There are a number of reasons why this is done, but
|
||||
they’re out of scope for this tutorial. There are some possible optimizations
|
||||
that don’t make it true 100% of the time, too. Rather than relying on the stack
|
||||
and `Drop` to clean up memory, the garbage collector deals with the heap
|
||||
instead.
|
||||
|
||||
# Which to use?
|
||||
|
||||
So if the stack is faster and easier to manage, why do we need the heap? A big
|
||||
reason is that Stack-allocation alone means you only have LIFO semantics for
|
||||
reclaiming storage. Heap-allocation is strictly more general, allowing storage
|
||||
to be taken from and returned to the pool in arbitrary order, but at a
|
||||
complexity cost.
|
||||
|
||||
Generally, you should prefer stack allocation, and so, Rust stack-allocates by
|
||||
default. The LIFO model of the stack is simpler, at a fundamental level. This
|
||||
has two big impacts: runtime efficiency and semantic impact.
|
||||
|
||||
## Runtime Efficiency.
|
||||
|
||||
Managing the memory for the stack is trivial: The machine just
|
||||
increments or decrements a single value, the so-called “stack pointer”.
|
||||
Managing memory for the heap is non-trivial: heap-allocated memory is freed at
|
||||
arbitrary points, and each block of heap-allocated memory can be of arbitrary
|
||||
size, the memory manager must generally work much harder to identify memory for
|
||||
reuse.
|
||||
|
||||
If you’d like to dive into this topic in greater detail, [this paper][wilson]
|
||||
is a great introduction.
|
||||
|
||||
[wilson]: http://www.cs.northwestern.edu/~pdinda/icsclass/doc/dsa.pdf
|
||||
|
||||
## Semantic impact
|
||||
|
||||
Stack-allocation impacts the Rust language itself, and thus the developer’s
|
||||
mental model. The LIFO semantics is what drives how the Rust language handles
|
||||
automatic memory management. Even the deallocation of a uniquely-owned
|
||||
heap-allocated box can be driven by the stack-based LIFO semantics, as
|
||||
discussed throughout this chapter. The flexibility (i.e. expressiveness) of non
|
||||
LIFO-semantics means that in general the compiler cannot automatically infer at
|
||||
compile-time where memory should be freed; it has to rely on dynamic protocols,
|
||||
potentially from outside the language itself, to drive deallocation (reference
|
||||
counting, as used by `Rc<T>` and `Arc<T>`, is one example of this).
|
||||
|
||||
When taken to the extreme, the increased expressive power of heap allocation
|
||||
comes at the cost of either significant runtime support (e.g. in the form of a
|
||||
garbage collector) or significant programmer effort (in the form of explicit
|
||||
memory management calls that require verification not provided by the Rust
|
||||
compiler).
|
||||
|
@ -8,6 +8,15 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use core::{isize, usize};
|
||||
|
||||
#[inline(always)]
|
||||
fn check_size_and_alignment(size: usize, align: usize) {
|
||||
debug_assert!(size != 0);
|
||||
debug_assert!(size <= isize::MAX as usize, "Tried to allocate too much: {} bytes", size);
|
||||
debug_assert!(usize::is_power_of_two(align), "Invalid alignment of allocation: {}", align);
|
||||
}
|
||||
|
||||
// FIXME: #13996: mark the `allocate` and `reallocate` return value as `noalias`
|
||||
|
||||
/// Return a pointer to `size` bytes of memory aligned to `align`.
|
||||
@ -19,6 +28,7 @@
|
||||
/// size on the platform.
|
||||
#[inline]
|
||||
pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
|
||||
check_size_and_alignment(size, align);
|
||||
imp::allocate(size, align)
|
||||
}
|
||||
|
||||
@ -38,6 +48,7 @@ pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
|
||||
/// any value in range_inclusive(requested_size, usable_size).
|
||||
#[inline]
|
||||
pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 {
|
||||
check_size_and_alignment(size, align);
|
||||
imp::reallocate(ptr, old_size, size, align)
|
||||
}
|
||||
|
||||
@ -56,6 +67,7 @@ pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usiz
|
||||
#[inline]
|
||||
pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: usize, size: usize,
|
||||
align: usize) -> usize {
|
||||
check_size_and_alignment(size, align);
|
||||
imp::reallocate_inplace(ptr, old_size, size, align)
|
||||
}
|
||||
|
||||
|
@ -1537,7 +1537,7 @@ impl BitSet {
|
||||
bit_vec.nbits = trunc_len * u32::BITS;
|
||||
}
|
||||
|
||||
/// Iterator over each u32 stored in the `BitSet`.
|
||||
/// Iterator over each usize stored in the `BitSet`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@ -1558,7 +1558,7 @@ impl BitSet {
|
||||
SetIter {set: self, next_idx: 0}
|
||||
}
|
||||
|
||||
/// Iterator over each u32 stored in `self` union `other`.
|
||||
/// Iterator over each usize stored in `self` union `other`.
|
||||
/// See [union_with](#method.union_with) for an efficient in-place version.
|
||||
///
|
||||
/// # Examples
|
||||
@ -1658,7 +1658,7 @@ impl BitSet {
|
||||
})
|
||||
}
|
||||
|
||||
/// Iterator over each u32 stored in the symmetric difference of `self` and `other`.
|
||||
/// Iterator over each usize stored in the symmetric difference of `self` and `other`.
|
||||
/// See [symmetric_difference_with](#method.symmetric_difference_with) for
|
||||
/// an efficient in-place version.
|
||||
///
|
||||
|
@ -745,6 +745,7 @@ variable.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
let x: i32 = "I am not a number!";
|
||||
// ~~~ ~~~~~~~~~~~~~~~~~~~~
|
||||
// | |
|
||||
@ -752,6 +753,7 @@ let x: i32 = "I am not a number!";
|
||||
// | compiler infers type `&str`
|
||||
// |
|
||||
// type `i32` assigned to variable `x`
|
||||
```
|
||||
"##,
|
||||
|
||||
E0309: r##"
|
||||
@ -760,6 +762,7 @@ how long the data stored within them is guaranteed to be live. This lifetime
|
||||
must be as long as the data needs to be alive, and missing the constraint that
|
||||
denotes this will cause this error.
|
||||
|
||||
```
|
||||
// This won't compile because T is not constrained, meaning the data
|
||||
// stored in it is not guaranteed to last as long as the reference
|
||||
struct Foo<'a, T> {
|
||||
@ -770,6 +773,7 @@ struct Foo<'a, T> {
|
||||
struct Foo<'a, T: 'a> {
|
||||
foo: &'a T
|
||||
}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0310: r##"
|
||||
@ -778,6 +782,7 @@ how long the data stored within them is guaranteed to be live. This lifetime
|
||||
must be as long as the data needs to be alive, and missing the constraint that
|
||||
denotes this will cause this error.
|
||||
|
||||
```
|
||||
// This won't compile because T is not constrained to the static lifetime
|
||||
// the reference needs
|
||||
struct Foo<T> {
|
||||
@ -788,6 +793,7 @@ struct Foo<T> {
|
||||
struct Foo<T: 'static> {
|
||||
foo: &'static T
|
||||
}
|
||||
```
|
||||
"##
|
||||
|
||||
}
|
||||
|
@ -1603,7 +1603,8 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
|
||||
Some(i as usize)),
|
||||
_ => {
|
||||
span_err!(tcx.sess, ast_ty.span, E0249,
|
||||
"expected constant expr for array length");
|
||||
"expected constant integer expression \
|
||||
for array length");
|
||||
this.tcx().types.err
|
||||
}
|
||||
}
|
||||
|
@ -150,6 +150,148 @@ attribute. Such a function must have the following type signature:
|
||||
```
|
||||
fn(isize, *const *const u8) -> isize
|
||||
```
|
||||
"##,
|
||||
|
||||
E0184: r##"
|
||||
Explicitly implementing both Drop and Copy for a type is currently disallowed.
|
||||
This feature can make some sense in theory, but the current implementation is
|
||||
incorrect and can lead to memory unsafety (see [issue #20126][iss20126]), so
|
||||
it has been disabled for now.
|
||||
|
||||
[iss20126]: https://github.com/rust-lang/rust/issues/20126
|
||||
"##,
|
||||
|
||||
E0204: r##"
|
||||
An attempt to implement the `Copy` trait for a struct failed because one of the
|
||||
fields does not implement `Copy`. To fix this, you must implement `Copy` for the
|
||||
mentioned field. Note that this may not be possible, as in the example of
|
||||
|
||||
```
|
||||
struct Foo {
|
||||
foo : Vec<u32>,
|
||||
}
|
||||
|
||||
impl Copy for Foo { }
|
||||
```
|
||||
|
||||
This fails because `Vec<T>` does not implement `Copy` for any `T`.
|
||||
|
||||
Here's another example that will fail:
|
||||
|
||||
```
|
||||
#[derive(Copy)]
|
||||
struct Foo<'a> {
|
||||
ty: &'a mut bool,
|
||||
}
|
||||
```
|
||||
|
||||
This fails because `&mut T` is not `Copy`, even when `T` is `Copy` (this
|
||||
differs from the behavior for `&T`, which is `Copy` when `T` is `Copy`).
|
||||
"##,
|
||||
|
||||
E0205: r##"
|
||||
An attempt to implement the `Copy` trait for an enum failed because one of the
|
||||
variants does not implement `Copy`. To fix this, you must implement `Copy` for
|
||||
the mentioned variant. Note that this may not be possible, as in the example of
|
||||
|
||||
```
|
||||
enum Foo {
|
||||
Bar(Vec<u32>),
|
||||
Baz,
|
||||
}
|
||||
|
||||
impl Copy for Foo { }
|
||||
```
|
||||
|
||||
This fails because `Vec<T>` does not implement `Copy` for any `T`.
|
||||
|
||||
Here's another example that will fail:
|
||||
|
||||
```
|
||||
#[derive(Copy)]
|
||||
enum Foo<'a> {
|
||||
Bar(&'a mut bool),
|
||||
Baz
|
||||
}
|
||||
```
|
||||
|
||||
This fails because `&mut T` is not `Copy`, even when `T` is `Copy` (this
|
||||
differs from the behavior for `&T`, which is `Copy` when `T` is `Copy`).
|
||||
"##,
|
||||
|
||||
E0206: r##"
|
||||
You can only implement `Copy` for a struct or enum. Both of the following
|
||||
examples will fail, because neither `i32` (primitive type) nor `&'static Bar`
|
||||
(reference to `Bar`) is a struct or enum:
|
||||
|
||||
```
|
||||
type Foo = i32;
|
||||
impl Copy for Foo { } // error
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Bar;
|
||||
impl Copy for &'static Bar { } // error
|
||||
```
|
||||
"##,
|
||||
|
||||
E0243: r##"
|
||||
This error indicates that not enough type parameters were found in a type or
|
||||
trait.
|
||||
|
||||
For example, the `Foo` struct below is defined to be generic in `T`, but the
|
||||
type parameter is missing in the definition of `Bar`:
|
||||
|
||||
```
|
||||
struct Foo<T> { x: T }
|
||||
|
||||
struct Bar { x: Foo }
|
||||
```
|
||||
"##,
|
||||
|
||||
E0244: r##"
|
||||
This error indicates that too many type parameters were found in a type or
|
||||
trait.
|
||||
|
||||
For example, the `Foo` struct below has no type parameters, but is supplied
|
||||
with two in the definition of `Bar`:
|
||||
|
||||
```
|
||||
struct Foo { x: bool }
|
||||
|
||||
struct Bar<S, T> { x: Foo<S, T> }
|
||||
```
|
||||
"##,
|
||||
|
||||
E0249: r##"
|
||||
This error indicates a constant expression for the array length was found, but
|
||||
it was not an integer (signed or unsigned) expression.
|
||||
|
||||
Some examples of code that produces this error are:
|
||||
|
||||
```
|
||||
const A: [u32; "hello"] = []; // error
|
||||
const B: [u32; true] = []; // error
|
||||
const C: [u32; 0.0] = []; // error
|
||||
"##,
|
||||
|
||||
E0250: r##"
|
||||
This means there was an error while evaluating the expression for the length of
|
||||
a fixed-size array type.
|
||||
|
||||
Some examples of code that produces this error are:
|
||||
|
||||
```
|
||||
// divide by zero in the length expression
|
||||
const A: [u32; 1/0] = [];
|
||||
|
||||
// Rust currently will not evaluate the function `foo` at compile time
|
||||
fn foo() -> usize { 12 }
|
||||
const B: [u32; foo()] = [];
|
||||
|
||||
// it is an error to try to add `u8` and `f64`
|
||||
use std::{f64, u8};
|
||||
const C: [u32; u8::MAX + f64::EPSILON] = [];
|
||||
```
|
||||
"##
|
||||
|
||||
}
|
||||
@ -164,18 +306,18 @@ register_diagnostics! {
|
||||
E0030,
|
||||
E0031,
|
||||
E0033,
|
||||
E0034,
|
||||
E0035,
|
||||
E0036,
|
||||
E0038,
|
||||
E0034, // multiple applicable methods in scope
|
||||
E0035, // does not take type parameters
|
||||
E0036, // incorrect number of type parameters given for this method
|
||||
E0038, // cannot convert to a trait object because trait is not object-safe
|
||||
E0040, // explicit use of destructor method
|
||||
E0044,
|
||||
E0045,
|
||||
E0044, // foreign items may not have type parameters
|
||||
E0045, // variadic function must have C calling convention
|
||||
E0049,
|
||||
E0050,
|
||||
E0053,
|
||||
E0055,
|
||||
E0057,
|
||||
E0055, // method has an incompatible type for trait
|
||||
E0057, // method has an incompatible type for trait
|
||||
E0059,
|
||||
E0060,
|
||||
E0061,
|
||||
@ -232,7 +374,6 @@ register_diagnostics! {
|
||||
E0178,
|
||||
E0182,
|
||||
E0183,
|
||||
E0184,
|
||||
E0185,
|
||||
E0186,
|
||||
E0187, // can't infer the kind of the closure
|
||||
@ -254,12 +395,6 @@ register_diagnostics! {
|
||||
E0202, // associated items are not allowed in inherent impls
|
||||
E0203, // type parameter has more than one relaxed default bound,
|
||||
// and only one is supported
|
||||
E0204, // trait `Copy` may not be implemented for this type; field
|
||||
// does not implement `Copy`
|
||||
E0205, // trait `Copy` may not be implemented for this type; variant
|
||||
// does not implement `copy`
|
||||
E0206, // trait `Copy` may not be implemented for this type; type is
|
||||
// not a structure or enumeration
|
||||
E0207, // type parameter is not constrained by the impl trait, self type, or predicate
|
||||
E0208,
|
||||
E0209, // builtin traits can only be implemented on structs or enums
|
||||
@ -296,14 +431,10 @@ register_diagnostics! {
|
||||
E0240,
|
||||
E0241,
|
||||
E0242, // internal error looking up a definition
|
||||
E0243, // wrong number of type arguments
|
||||
E0244, // wrong number of type arguments
|
||||
E0245, // not a trait
|
||||
E0246, // illegal recursive type
|
||||
E0247, // found module name used as a type
|
||||
E0248, // found value name used as a type
|
||||
E0249, // expected constant expr for array length
|
||||
E0250, // expected constant expr for array length
|
||||
E0318, // can't create default impls for traits outside their crates
|
||||
E0319, // trait impls for defaulted traits allowed just for structs/enums
|
||||
E0320, // recursive overflow during dropck
|
||||
|
@ -1199,7 +1199,7 @@ impl Into<OsString> for PathBuf {
|
||||
/// absolute, and so on. More details about the overall approach can be found in
|
||||
/// the module documentation.
|
||||
///
|
||||
/// This is an *unsized* type, meaning that it must always be used with behind a
|
||||
/// This is an *unsized* type, meaning that it must always be used behind a
|
||||
/// pointer like `&` or `Box`.
|
||||
///
|
||||
/// # Examples
|
||||
|
Loading…
Reference in New Issue
Block a user