Skip to content

Commit 99ca153

Browse files
committed
Finished ch4
1 parent f3dfbf3 commit 99ca153

File tree

3 files changed

+246
-1
lines changed

3 files changed

+246
-1
lines changed
Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,102 @@
11
fn main() {
2-
println!("Hello, world!");
2+
show_rules();
3+
test_calculate_length();
4+
test_change();
5+
illegal_scope_too_many_mut_1();
6+
illegal_scope_too_many_mut_2();
7+
legal_scope_too_many_mut_1();
8+
legal_scope_too_many_mut_2();
9+
avoiding_dangle();
310
}
11+
12+
fn show_rules() {
13+
print!("================================================================\n\
14+
The Rules of References:\n\
15+
- At any given time, you can have either (but not both of) one\n \
16+
mutable reference or any number of immutable references.\n\
17+
- References must always be valid.\n\
18+
================================================================\n")
19+
}
20+
21+
22+
fn test_calculate_length() {
23+
let s1 = String::from("Hello");
24+
let len = calculate_length(&s1);
25+
println!("The length of '{}' is {}.", s1, len); // s1 not moved - yay!
26+
27+
}
28+
29+
fn calculate_length(s: &String) -> usize { // s is a reference to a String
30+
s.len()
31+
} // Here, s goes out of scope. But because it does not have ownership of what
32+
// it refers to, nothing happens.
33+
34+
fn test_change() {
35+
// let immutable_str = String::from("Hello");
36+
// illegal_change(&immutable_str);
37+
38+
let mut mutable_str = String::from("Hello");
39+
legal_change(&mut mutable_str);
40+
println!("{}", mutable_str);
41+
42+
}
43+
44+
/*
45+
fn illegal_change(some_string: &String) {
46+
some_string.push_str(", world!"); // not mutable!
47+
}
48+
*/
49+
50+
fn legal_change(some_string: &mut String) {
51+
some_string.push_str(", world!");
52+
}
53+
54+
// Rust only allows one mutable reference to a particular piece of data in a
55+
// particular scope, to prevent data races
56+
fn illegal_scope_too_many_mut_1() {
57+
let mut s = String::from("Hello");
58+
let _r1 = &mut s; // all good
59+
// let _r2 = &mut s; Illegal
60+
}
61+
62+
// Mixing mutable and immutable references is also not allowed
63+
#[allow(unused_mut)] // ignore compiler warnings for the unused mutable
64+
fn illegal_scope_too_many_mut_2() {
65+
let mut s = String::from("Hello");
66+
let _r1 = &s; // all good
67+
let _r2 = &s; // all good
68+
// let _r3 = &mut s; Illegal
69+
}
70+
71+
fn legal_scope_too_many_mut_1() {
72+
let mut s = String::from("Hello");
73+
{
74+
let _r1 = &mut s;
75+
} // _r1 now out of scope, so we can now do:
76+
let _r2 = &mut s;
77+
}
78+
79+
#[allow(unused_mut)] // ignore compiler warnings for the unused mutable
80+
fn legal_scope_too_many_mut_2() {
81+
let mut s = String::from("Hello");
82+
let _r1 = &s;
83+
let _r2 = &s;
84+
// In this example we are wasting the mutability of s, but it works fine
85+
// and is allowed.
86+
}
87+
88+
/*
89+
fn illegal_dangle() -> &String { // dangle returns a reference to a String
90+
91+
let s = String::from("hello"); // s is a new String
92+
93+
&s // we return a reference to the String, s
94+
} // Here, s goes out of scope, and is dropped. Its memory goes away. Danger!
95+
*/
96+
97+
// Just return the String directly.
98+
fn avoiding_dangle() -> String {
99+
let s = String::from("Hello");
100+
101+
s
102+
}

ch4_slices/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "ch4_slices"
3+
version = "0.1.0"
4+
authors = ["matty <mattypowell101@gmail.com>"]
5+
6+
[dependencies]

ch4_slices/src/main.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Note: String slice range indices must occur at valid UTF-8 character
2+
// boundaries. If you attempt to create a string slice in the middle of a
3+
// multibyte character, your program will exit with an error. For the purposes
4+
// of introducing string slices, we are assuming ASCII only in this section;
5+
// a more thorough discussion of UTF-8 handling is found in Chapter 8.
6+
7+
8+
fn main() {
9+
test_first_word();
10+
intro_to_string_slices();
11+
test_better_first_word();
12+
test_even_better_first_word();
13+
slice_array();
14+
}
15+
16+
17+
// Returns the index of the first space in the given string, or if no spaces are
18+
// found, return it's length
19+
fn first_word(s: &String) -> usize {
20+
let bytes = s.as_bytes(); // Gets String as array of bytes
21+
22+
for (i, &item) in bytes.iter().enumerate() { // iterate with index
23+
// & needed because iter().enumerate() gives us a reference only
24+
if item == b' ' { // byte literal of space
25+
return i; // space found, return its index
26+
}
27+
}
28+
29+
s.len() // no spaces found, return whole String's length
30+
}
31+
32+
// Our goal is to use this to get and be able to use the first word of a String
33+
fn test_first_word() {
34+
let mut s = String::from("Hello");
35+
let _word = first_word(&s); // _word = 5. looking good so far. We can use
36+
// this along with s to get the first word of s
37+
38+
s.clear(); // s now equals ""
39+
40+
// here, _word still has the value 5 (obviously), but s has changed, making
41+
// _word useless to us now. We really need a way to actually store a new
42+
// String that is just the first word.
43+
}
44+
45+
// This get's even more difficult to deal with if we needed to get the second
46+
// word: we'd need to track a starting and an ending index:
47+
// fn second_word(s: &String) -> (usize, usize) { ...
48+
// So let's try using String slices instead:
49+
50+
fn intro_to_string_slices() {
51+
let s = String::from("hello world");
52+
53+
let _hello_part = &s[0..5];
54+
let _world_part = &s[6..11];
55+
56+
// Alternatively:
57+
let _hello_part_inclusive_of_end = &s[0..=4];
58+
let _world_part_inclusive_of_end = &s[6..=10];
59+
60+
// Alternatively:
61+
let _hello_part_starting_from_zero = &s[..4]; // don't need 0 at start
62+
let _world_part_ending_at_len_of_s = &s[6..]; // don't need len at end
63+
64+
// Or for whole string:
65+
let _whole_slice = [..];
66+
}
67+
68+
// Also would work for a second_word() function
69+
fn better_first_word(s: &String) -> &str { // &str is 'string slice'
70+
let bytes = s.as_bytes();
71+
72+
for (i, &item) in bytes.iter().enumerate() {
73+
if item == b' ' {
74+
return &s[..i]; // return s from 0 to index of first space
75+
// (not inclusive)
76+
}
77+
}
78+
&s[..] // No spaces found, return whole string.
79+
}
80+
81+
#[allow(unused_mut)] // ignore compiler warnings for the unused mutable
82+
fn test_better_first_word() {
83+
let mut s = String::from("hello world"); // immutable borrow on s starts
84+
85+
let _word = better_first_word(&s);
86+
87+
// s.clear(); // Compile error here - that's much better than only finding
88+
// out that your index var is useless when you try to use it
89+
// ^ attempted mutable borrow!!! But as we recall, you can't mutably borrow
90+
// something we have already borrowed!
91+
// (mutably or not)
92+
} // immutable borrow on s ends
93+
94+
// Exact same code as better_first_word, but different signature: note that it
95+
// takes an &str as the param rather than a &String
96+
fn even_better_first_word(s: &str) -> &str {
97+
let bytes = s.as_bytes();
98+
99+
for (i, &item) in bytes.iter().enumerate() {
100+
if item == b' ' {
101+
return &s[..i]; // return s from 0 to index of first space
102+
// (not inclusive)
103+
}
104+
}
105+
&s[..] // No spaces found, return whole string.
106+
}
107+
108+
fn test_even_better_first_word() {
109+
let my_string = String::from("hello world");
110+
111+
// first_word works on slices of Strings
112+
let word1 = even_better_first_word(&my_string[..]);
113+
114+
// Note: It's not in the tutorial but it also works with regular Strings
115+
// because &String can dereference to &str (because String implements
116+
// Deref<Target=str>). This is called 'deref coercions'.
117+
// Basically &A can coerce to &B if A implements Deref<B>.
118+
let word2 = even_better_first_word(&my_string);
119+
120+
let my_string_literal = "hello world";
121+
122+
// first_word works on slices of string literals
123+
let word3 = even_better_first_word(&my_string_literal[..]);
124+
125+
// Because string literals *are* string slices already,
126+
// this works too, without the slice syntax!
127+
let word4 = even_better_first_word(my_string_literal);
128+
129+
println!("The value of word1 is: '{}'", word1);
130+
println!("The value of word2 is: '{}'", word2);
131+
println!("The value of word3 is: '{}'", word3);
132+
println!("The value of word4 is: '{}'", word4);
133+
}
134+
135+
fn slice_array() {
136+
let a = [1, 2, 3, 4, 5];
137+
let slice = &a[1..3];
138+
println!("a = {:?}", a);
139+
println!("&a[1..3] = {:?}", slice);
140+
}

0 commit comments

Comments
 (0)