forked from rust-lang/book
-
Notifications
You must be signed in to change notification settings - Fork 106
/
Copy pathch04-01-ownership-sec2-moves.toml
119 lines (113 loc) · 3.14 KB
/
ch04-01-ownership-sec2-moves.toml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
[[questions]]
id = "889ca9ac-0f4b-4abf-82c4-be9cd4d4f7ad"
type = "MultipleChoice"
prompt.prompt = "Which of the following is NOT a kind of undefined behavior?"
answer.answer = "Having a pointer to freed memory in a stack frame"
prompt.distractors = [
"Using a pointer that points to freed memory",
"Freeing the same memory a second time",
"Using a non-boolean value as an `if` condition"
]
context = """
It can be perfectly safe to have a pointer to freed memory in a stack frame.
The important thing is to not *use* that pointer again, e.g. by reading it or freeing it.
"""
[[questions]]
id = "94425a4e-043e-47f8-9fba-c5b09fb60aa3"
type = "Tracing"
prompt.program = """
fn add_suffix(mut s: String) -> String {
s.push_str(" world");
s
}
fn main() {
let s = String::from("hello");
let s2 = add_suffix(s);
println!("{}", s2);
}
"""
answer.doesCompile = true
answer.stdout = "hello world"
context = """
This program is valid because `s` is not used after moving it into `add_suffix`.
"""
[[questions]]
id = "c00e3638-861b-4f9e-a55c-bfb67af79280"
type = "Tracing"
prompt.program = """
fn main() {
let s = String::from("hello");
let s2;
let b = false;
if b {
s2 = s;
}
println!("{}", s);
}
"""
answer.doesCompile = false
answer.lineNumber = 8
context = """
Because `s` could be moved inside of the if-statement, it is illegal to use it on line 8.
While the if-statement will never execute in this program because `b` is always `false`,
Rust does not in general try to determine whether if-statements will or won't execute. Rust just
assumes that it *might* be executed, and therefore `s` *might* be moved.
"""
[[questions]]
id = "f7d67c11-60bb-4d92-8cc2-8ce82cf4c974"
type = "MultipleChoice"
prompt.prompt = """
Say we have a function that moves a box, like this:
```
fn move_a_box(b: Box<i32>) {
// This space intentionally left blank
}
```
Below are four snippets which are rejected by the Rust compiler.
Imagine that Rust instead allowed these snippets to compile and run.
Select each snippet that would cause undefined behavior, or select
"None of these snippets" if none of these snippets would cause undefined behavior.
"""
answer.answer = [
"""
```
let b = Box::new(0);
let b2 = b;
move_a_box(b);
```
""",
"""
```
let b = Box::new(0);
move_a_box(b);
println!("{}", b);
```
""",
"""
```
let b = Box::new(0);
move_a_box(b);
let b2 = b;
```
"""
]
prompt.distractors = [
"""
```
let b = Box::new(0);
let b2 = b;
println!("{}", b);
move_a_box(b2);
```
""",
"None of these snippets"
]
context = """
The key idea is that when a box is passed to `move_a_box`, its memory is deallocated after `move_a_box` ends.
Therefore:
* Reading `b` via `println` after `move_a_box` is undefined behavior, as it reads freed memory.
* Giving `b` a second owner is undefined behavior, as it would cause Rust to free the box a second time on behalf of `b2`. It doesn't matter whether the `let b2 = b` binding happens
before or after `move_a_box`.
However, doing `let b2 = b` and then `println` is not undefined behavior. Although `b` is moved, its data is not deallocated until `move_a_box` is called at the end. Therefore
this program is technically safe, although still rejected by Rust.
"""