Skip to content

Commit

Permalink
Add a short discussion of assignment and ownership in ch. 04
Browse files Browse the repository at this point in the history
Add a short new section showing how assignment to a mutable variable
causes an existing *owned* binding to be freed immediately. Create a new
code sample and a new diagram to illustrate the behavior.

Fixes #4001.
  • Loading branch information
chriskrycho committed Oct 1, 2024
1 parent 09239f3 commit 9eed938
Show file tree
Hide file tree
Showing 12 changed files with 432 additions and 227 deletions.
85 changes: 54 additions & 31 deletions dot/trpl04-05.dot
Original file line number Diff line number Diff line change
@@ -1,32 +1,55 @@
digraph {
rankdir=LR;
overlap=false;
dpi=300.0;
node [shape="plaintext"];

table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="2" SIDES="B">s</TD></TR>
<TR><TD>name</TD><TD>value</TD></TR>
<TR><TD>ptr</TD><TD PORT="borrower"></TD></TR>
</TABLE>>];
table1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="2" SIDES="B">s1</TD></TR>
<TR><TD>name</TD><TD>value</TD></TR>
<TR><TD PORT="borrowee">ptr</TD><TD PORT="pointer"></TD></TR>
<TR><TD>len</TD><TD>5</TD></TR>
<TR><TD>capacity</TD><TD>5</TD></TR>
</TABLE>>];
table2[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD>index</TD><TD>value</TD></TR>
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
<TR><TD>1</TD><TD>e</TD></TR>
<TR><TD>2</TD><TD>l</TD></TR>
<TR><TD>3</TD><TD>l</TD></TR>
<TR><TD>4</TD><TD>o</TD></TR>
</TABLE>>];

edge[tailclip="false"];
table1:pointer:c -> table2:pointee;
table0:borrower:c -> table1:borrowee;
}

rankdir = LR;
overlap = false;
newrank = true;
dpi = 300.0;
splines = false;
clusterrank = "local";
node [shape = "plaintext";];

subgraph cluster_stack {
peripheries = 0;
rank = "same";

// Just to have the right height!
a [label = <<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" BGCOLOR="gray">
<TR><TD>a</TD></TR>
<TR><TD>0</TD></TR>
<TR><TD>1</TD></TR>
<TR><TD>2</TD></TR>
<TR><TD>3</TD></TR>
</TABLE>>;style = invis;];

s [label = <<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="2" SIDES="B">s</TD></TR>
<TR><TD>name</TD><TD>value</TD></TR>
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
<TR><TD>len</TD><TD>4</TD></TR>
<TR><TD>capacity</TD><TD>4</TD></TR>
</TABLE>>;];
}

subgraph cluster_heap {
peripheries = 0;
rank = "same";

hello [label = <<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" BGCOLOR="gray">
<TR><TD>index</TD><TD>value</TD></TR>
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
<TR><TD>1</TD><TD>e</TD></TR>
<TR><TD>2</TD><TD>l</TD></TR>
<TR><TD>3</TD><TD>l</TD></TR>
<TR><TD>4</TD><TD>o</TD></TR>
</TABLE>>;];

ahoy [label = <<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD>index</TD><TD>value</TD></TR>
<TR><TD PORT="pointee">0</TD><TD>a</TD></TR>
<TR><TD>1</TD><TD>h</TD></TR>
<TR><TD>2</TD><TD>o</TD></TR>
<TR><TD>3</TD><TD>y</TD></TR>
</TABLE>>;];
}

s -> ahoy [tailport = "pointer:c"; headport = "pointee"; tailclip = false;];
}
29 changes: 10 additions & 19 deletions dot/trpl04-06.dot
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,28 @@ digraph {
node [shape="plaintext"];

table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="2" SIDES="B">world</TD></TR>
<TR><TD COLSPAN="2" SIDES="B">s</TD></TR>
<TR><TD>name</TD><TD>value</TD></TR>
<TR><TD>ptr</TD><TD PORT="pointer2"></TD></TR>
<TR><TD>len</TD><TD>5</TD></TR>
<TR><TD>ptr</TD><TD PORT="borrower"></TD></TR>
</TABLE>>];

table3[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="2" SIDES="B">s</TD></TR>
table1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="2" SIDES="B">s1</TD></TR>
<TR><TD>name</TD><TD>value</TD></TR>
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
<TR><TD>len</TD><TD>11</TD></TR>
<TR><TD>capacity</TD><TD>11</TD></TR>
<TR><TD PORT="borrowee">ptr</TD><TD PORT="pointer"></TD></TR>
<TR><TD>len</TD><TD>5</TD></TR>
<TR><TD>capacity</TD><TD>5</TD></TR>
</TABLE>>];
table4[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
table2[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD>index</TD><TD>value</TD></TR>
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
<TR><TD>1</TD><TD>e</TD></TR>
<TR><TD>2</TD><TD>l</TD></TR>
<TR><TD>3</TD><TD>l</TD></TR>
<TR><TD>4</TD><TD>o</TD></TR>
<TR><TD>5</TD><TD> </TD></TR>
<TR><TD PORT="pointee2">6</TD><TD>w</TD></TR>
<TR><TD>7</TD><TD>o</TD></TR>
<TR><TD>8</TD><TD>r</TD></TR>
<TR><TD>9</TD><TD>l</TD></TR>
<TR><TD>10</TD><TD>d</TD></TR>
</TABLE>>];


edge[tailclip="false"];
table0:pointer2:c -> table4:pointee2;
table3:pointer:c -> table4:pointee;
table1:pointer:c -> table2:pointee;
table0:borrower:c -> table1:borrowee;
}

41 changes: 41 additions & 0 deletions dot/trpl04-07.dot
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
digraph {
rankdir=LR;
overlap=false;
dpi=300.0;
node [shape="plaintext"];

table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="2" SIDES="B">world</TD></TR>
<TR><TD>name</TD><TD>value</TD></TR>
<TR><TD>ptr</TD><TD PORT="pointer2"></TD></TR>
<TR><TD>len</TD><TD>5</TD></TR>
</TABLE>>];

table3[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="2" SIDES="B">s</TD></TR>
<TR><TD>name</TD><TD>value</TD></TR>
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
<TR><TD>len</TD><TD>11</TD></TR>
<TR><TD>capacity</TD><TD>11</TD></TR>
</TABLE>>];
table4[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD>index</TD><TD>value</TD></TR>
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
<TR><TD>1</TD><TD>e</TD></TR>
<TR><TD>2</TD><TD>l</TD></TR>
<TR><TD>3</TD><TD>l</TD></TR>
<TR><TD>4</TD><TD>o</TD></TR>
<TR><TD>5</TD><TD> </TD></TR>
<TR><TD PORT="pointee2">6</TD><TD>w</TD></TR>
<TR><TD>7</TD><TD>o</TD></TR>
<TR><TD>8</TD><TD>r</TD></TR>
<TR><TD>9</TD><TD>l</TD></TR>
<TR><TD>10</TD><TD>d</TD></TR>
</TABLE>>];


edge[tailclip="false"];
table0:pointer2:c -> table4:pointee2;
table3:pointer:c -> table4:pointee;
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "ownership"
version = "0.1.0"
edition = "2021"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fn main() {
// ANCHOR: here
let mut s = String::from("hello");
s = String::from("ahoy");

println!("{s}, world!");
// ANCHOR_END: here
}
31 changes: 31 additions & 0 deletions src/ch04-01-what-is-ownership.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,37 @@ In addition, there’s a design choice that’s implied by this: Rust will never
automatically create “deep” copies of your data. Therefore, any *automatic*
copying can be assumed to be inexpensive in terms of runtime performance.

#### Scope and Assignment

The inverse of this is true for the relationship between scoping, ownership, and
memory being freed via the `drop` function as well. When you assign a completely
new value to an existing variable, Rust will call `drop` and free the original
value’s memory immediately. Consider this code, for example:

```rust
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-04b-replacement-drop/src/main.rs:here}}
```

We initially declare a variable `s` and bind it to a `String` with the value
`"hello"`. Then we immediately create a new `String` with the value `"ahoy"` and
assign it to `s`. At this point, nothing is referring to the original value on
the heap at all.

<img alt="One table s representing the string value on the stack, pointing to
the second piece of string data (ahoy) on the heap, with the original string
data (hello) grayed out because it cannot be accessed anymore."
src="img/trpl04-05.svg"
class="center"
style="width: 50%;"
/>

<span class="caption">Figure 4-4: Representation in memory after `s1` has been
invalidated</span>

The original string thus immediately goes out of scope. Rust will run the `drop`
function on it and its memory will be freed right away. When we print the value
at the end, it will be `"ahoy, world!"`.

<!-- Old heading. Do not remove or links may break. -->
<a id="ways-variables-and-data-interact-clone"></a>

Expand Down
6 changes: 3 additions & 3 deletions src/ch04-02-references-and-borrowing.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ First, notice that all the tuple code in the variable declaration and the
function return value is gone. Second, note that we pass `&s1` into
`calculate_length` and, in its definition, we take `&String` rather than
`String`. These ampersands represent *references*, and they allow you to refer
to some value without taking ownership of it. Figure 4-5 depicts this concept.
to some value without taking ownership of it. Figure 4-6 depicts this concept.

<img alt="Three tables: the table for s contains only a pointer to the table
for s1. The table for s1 contains the stack data for s1 and points to the
string data on the heap." src="img/trpl04-05.svg" class="center" />
string data on the heap." src="img/trpl04-06.svg" class="center" />

<span class="caption">Figure 4-5: A diagram of `&String s` pointing at `String
<span class="caption">Figure 4-6: A diagram of `&String s` pointing at `String
s1`</span>

> Note: The opposite of referencing by using `&` is *dereferencing*, which is
Expand Down
6 changes: 3 additions & 3 deletions src/ch04-03-slices.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,15 @@ corresponds to `ending_index` minus `starting_index`. So, in the case of `let
world = &s[6..11];`, `world` would be a slice that contains a pointer to the
byte at index 6 of `s` with a length value of `5`.

Figure 4-6 shows this in a diagram.
Figure 4-7 shows this in a diagram.

<img alt="Three tables: a table representing the stack data of s, which points
to the byte at index 0 in a table of the string data &quot;hello world&quot; on
the heap. The third table rep-resents the stack data of the slice world, which
has a length value of 5 and points to byte 6 of the heap data table."
src="img/trpl04-06.svg" class="center" style="width: 50%;" />
src="img/trpl04-07.svg" class="center" style="width: 50%;" />

<span class="caption">Figure 4-6: String slice referring to part of a
<span class="caption">Figure 4-7: String slice referring to part of a
`String`</span>

With Rust’s `..` range syntax, if you want to start at index 0, you can drop
Expand Down
Loading

0 comments on commit 9eed938

Please sign in to comment.