Skip to content

Commit f3dfbf3

Browse files
committed
Up to ch4 references and borrowing now
1 parent 9a25bf0 commit f3dfbf3

File tree

4 files changed

+198
-0
lines changed

4 files changed

+198
-0
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "ch4_ownership_scope_memory"
3+
version = "0.1.0"
4+
authors = ["matty <mattypowell101@gmail.com>"]
5+
6+
[dependencies]
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
fn main() {
2+
show_rules();
3+
scope_example();
4+
intro_to_strings();
5+
scope_and_the_heap();
6+
good_int_copy();
7+
bad_string_copy();
8+
good_string_copy();
9+
10+
ownership_scope1();
11+
ownership_scope2();
12+
return_multiple_values();
13+
}
14+
15+
fn show_rules() {
16+
println!("===============================================================");
17+
println!("Ownership Rules of Rust:");
18+
print!("1. Each value in Rust has a variable that’s called its owner.\n\
19+
2. There can only be one owner at a time.\n\
20+
3. When the owner goes out of scope, the value will be dropped.\n");
21+
println!("===============================================================");
22+
}
23+
24+
fn scope_example() {
25+
// _s is not valid here, it’s not yet declared
26+
{
27+
// _s is not valid here, it’s not yet declared
28+
let _s = "hello";
29+
// _s is valid from this point forward
30+
}
31+
// this scope is now over, and _s is no longer valid
32+
}
33+
34+
fn intro_to_strings() {
35+
// Declare: immutable &str (string literal/slice) on stack
36+
let _immutable_stack_string = "Hello";
37+
38+
// Declare mutable String on heap
39+
let mut mutable_heap_string = String::from("Hello");
40+
// Appends an &str to a String
41+
mutable_heap_string.push_str(", world!");
42+
println!("{}", mutable_heap_string);
43+
}
44+
45+
fn scope_and_the_heap() {
46+
// _s is not valid here, it’s not yet declared
47+
{
48+
let _s = String::from("hello");
49+
// _s is valid from this point forward
50+
}
51+
// this scope is now over, and _s is no longer valid
52+
// BUT the memory is automatically returned once the variable that owns it
53+
// goes out of scope. This is called the "Drop" trait.
54+
}
55+
56+
fn good_int_copy() {
57+
let x = 5;
58+
let y = x;
59+
println!("y = {}", y);
60+
println!("x = {}", x);
61+
// Seems reasonable. Works fine. Let's try it with a String:
62+
// see bad_string_copy()
63+
}
64+
65+
fn bad_string_copy() {
66+
let s1 = String::from("hello");
67+
let s2 = s1;
68+
println!("s2 = {}", s2); // compiles fine up to here
69+
// println!("{}", s1); Error here!
70+
71+
// Why? Because on the line let s2 = s1;, s2 replaces s1.
72+
// ie. The memory is copied, and then s1 is freed. This is called a "move".
73+
// (Think shallow copy, but then s1's reference is invalidated)
74+
// To actually copy the String (deep copy), we need some more work.
75+
// See good_string_copy().
76+
}
77+
78+
fn good_string_copy() {
79+
let s1 = String::from("hello");
80+
let s2 = s1.clone();
81+
println!("s2 = {}", s2); // s2 points to a new set of data (deep copy of s1)
82+
println!("s1 = {}", s1); // s1 still points to original set of data
83+
// all okay!
84+
85+
// So why does good_int_copy() work?
86+
// It's because the integers are on the stack, and have a known size at
87+
// compile time so copies of the values are quick to make.
88+
// ie. integers have "copy" trait, which means original reference is still
89+
// valid even after a direct copy (with =).
90+
//
91+
// Rust won’t let us annotate a type with the Copy trait if the type,
92+
// or any of its parts, has implemented the Drop trait. If the type needs
93+
// something special to happen when the value goes out of scope and we add
94+
// the Copy annotation to that type, we’ll get a compile time error.
95+
//
96+
// Other types with the copy trait:
97+
// - all integers
98+
// - all floats
99+
// - bool
100+
// - char
101+
// - Tuples, but only if they contain types that are also Copy.
102+
// ie. (i32, i32) is Copy, but (i32, String) is not.
103+
}
104+
105+
106+
//======================= Ownership Example 1 ==================================
107+
fn ownership_scope1() {
108+
let s = String::from("hello"); // s comes into scope
109+
110+
takes_ownership(s); // s's value moves into the function...
111+
// ... and so is no longer valid here
112+
113+
let x = 5; // x comes into scope
114+
115+
makes_copy(x); // x would move into the function,
116+
// but i32 is Copy, so it’s okay to still
117+
// use x afterward
118+
119+
} // Here, x goes out of scope, then s. But because s's value was moved, nothing
120+
// special happens.
121+
122+
fn takes_ownership(some_string: String) { // some_string comes into scope
123+
println!("{}", some_string);
124+
} // Here, some_string goes out of scope and `drop` is called. The backing
125+
// memory is freed.
126+
127+
fn makes_copy(some_integer: i32) { // some_integer comes into scope
128+
println!("{}", some_integer);
129+
} // Here, some_integer goes out of scope. Nothing special happens.
130+
131+
132+
//======================= Ownership Example 2 ==================================
133+
fn ownership_scope2() {
134+
let _s1 = gives_ownership(); // gives_ownership moves its return val into s1
135+
136+
let s2 = String::from("hello"); // s2 comes into scope
137+
138+
let _s3 = takes_and_gives_back(s2); // s2 is moved into
139+
// takes_and_gives_back, which
140+
// also moves its return value
141+
// into s3
142+
143+
} // Here, s3 goes out of scope and is dropped. s2 goes out of scope but was
144+
// moved, so nothing happens. s1 goes out of scope and is dropped.
145+
146+
fn gives_ownership() -> String { // gives_ownership will move its return value
147+
// into the function that calls it
148+
149+
let some_string = String::from("hello"); // some_string comes into scope
150+
151+
some_string // some_string is returned and
152+
// moves out to the calling
153+
// function
154+
}
155+
156+
// takes_and_gives_back will take a String and return one.
157+
fn takes_and_gives_back(a_string: String) -> String { // a_string comes into
158+
// scope
159+
160+
a_string // a_string is returned and moves out to the calling function
161+
}
162+
163+
//======================= Returning multiple values ============================
164+
fn return_multiple_values() {
165+
let s1 = String::from("hello");
166+
167+
let (s2, len) = calculate_length(s1);
168+
// This is basically just a hack to get around the fact that ownership of
169+
// s1 will be lost to calculate_length.
170+
171+
println!("The length of '{}' is {}.", s2, len);
172+
}
173+
174+
fn calculate_length(s: String) -> (String, usize) {
175+
let length = s.len(); // len() returns the length of a String
176+
177+
(s, length) // return the length, but also s, to (in a very hacky way) give
178+
// back the String we stole from the function which called this.
179+
}
180+
181+
// So, to summarize. This works, but is too much ceremony, and personally I
182+
// think it seems rather hacky, thus the need for... references!
183+
// (see ch4_references_and_borrowing)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "ch4_references_and_borrowing"
3+
version = "0.1.0"
4+
authors = ["matty <mattypowell101@gmail.com>"]
5+
6+
[dependencies]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
println!("Hello, world!");
3+
}

0 commit comments

Comments
 (0)