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