diff --git a/ci/dictionary.txt b/ci/dictionary.txt index 565c934f13..085222ee4b 100644 --- a/ci/dictionary.txt +++ b/ci/dictionary.txt @@ -630,6 +630,7 @@ WeatherForecast webpage WebSocket whitespace +whitespaces wildcard wildcards Wirth diff --git a/listings/ch04-understanding-ownership/listing-04-07/src/main.rs b/listings/ch04-understanding-ownership/listing-04-07/src/main.rs index 3bb3c8580d..694595f637 100644 --- a/listings/ch04-understanding-ownership/listing-04-07/src/main.rs +++ b/listings/ch04-understanding-ownership/listing-04-07/src/main.rs @@ -1,14 +1,13 @@ // ANCHOR: here fn first_word(s: &String) -> usize { - // ANCHOR: as_bytes - let bytes = s.as_bytes(); - // ANCHOR_END: as_bytes - + // ANCHOR: char_indices + let char_indices = s.char_indices(); + // ANCHOR_END: char_indices // ANCHOR: iter - for (i, &item) in bytes.iter().enumerate() { + for (i, ch) in char_indices { // ANCHOR_END: iter // ANCHOR: inside_for - if item == b' ' { + if ch.is_whitespace() { return i; } } diff --git a/listings/ch04-understanding-ownership/listing-04-08/src/main.rs b/listings/ch04-understanding-ownership/listing-04-08/src/main.rs index c7b0d1354a..7fdfe05626 100644 --- a/listings/ch04-understanding-ownership/listing-04-08/src/main.rs +++ b/listings/ch04-understanding-ownership/listing-04-08/src/main.rs @@ -1,8 +1,6 @@ fn first_word(s: &String) -> usize { - let bytes = s.as_bytes(); - - for (i, &item) in bytes.iter().enumerate() { - if item == b' ' { + for (i, ch) in s.char_indices() { + if ch.is_whitespace() { return i; } } diff --git a/listings/ch04-understanding-ownership/listing-04-09/src/main.rs b/listings/ch04-understanding-ownership/listing-04-09/src/main.rs index 161b388544..a12da82d84 100644 --- a/listings/ch04-understanding-ownership/listing-04-09/src/main.rs +++ b/listings/ch04-understanding-ownership/listing-04-09/src/main.rs @@ -1,11 +1,10 @@ // ANCHOR: here fn first_word(s: &str) -> &str { // ANCHOR_END: here - let bytes = s.as_bytes(); - for (i, &item) in bytes.iter().enumerate() { - if item == b' ' { - return &s[0..i]; + for (i, ch) in s.char_indices() { + if ch.is_whitespace() { + return &s[..i]; } } diff --git a/listings/ch04-understanding-ownership/listing-04-10/Cargo.lock b/listings/ch04-understanding-ownership/listing-04-10/Cargo.lock new file mode 100755 index 0000000000..2aa4918e5d --- /dev/null +++ b/listings/ch04-understanding-ownership/listing-04-10/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ownership" +version = "0.1.0" + diff --git a/listings/ch04-understanding-ownership/listing-04-10/Cargo.toml b/listings/ch04-understanding-ownership/listing-04-10/Cargo.toml new file mode 100755 index 0000000000..a4b0049f1d --- /dev/null +++ b/listings/ch04-understanding-ownership/listing-04-10/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "ownership" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/listings/ch04-understanding-ownership/listing-04-10/src/main.rs b/listings/ch04-understanding-ownership/listing-04-10/src/main.rs new file mode 100755 index 0000000000..33cbf1b664 --- /dev/null +++ b/listings/ch04-understanding-ownership/listing-04-10/src/main.rs @@ -0,0 +1,62 @@ +// ANCHOR: here +fn nth_word(s: &str, n: usize) -> &str { + // The minimum acceptable value for `n` is 1. + if n == 0 { + return s; + } + let mut n = n; + let mut word_start_index = 0; + // This flag is set to `true` when skipping leading spaces or spaces between words + let mut skipping_spaces = true; + for (i, ch) in s.char_indices() { + if skipping_spaces { + if ch.is_whitespace() { + continue; + } + skipping_spaces = false; + word_start_index = i; + } else if ch.is_whitespace() { + // End of current word + n -= 1; + if n == 0 { + return &s[word_start_index..i]; + } + // Skip spaces before looking for the next word + skipping_spaces = true; + } else if i + 1 == s.len() && n == 1 { + // We are at end of `s` and are looking for the last word + return &s[word_start_index..]; + } + } + s +} +// ANCHOR_END: here + +// ANCHOR: usage +fn main() { + // Entire word should be returned when looking for 0th word or + // when looking for a word beyond words in the input + assert_eq!(nth_word("1 2 3", 0), "1 2 3"); + assert_eq!(nth_word("1 2 3 4", 5), "1 2 3 4"); + + // Input word should be returned when no word exists + assert_eq!(nth_word("", 1), ""); + assert_eq!(nth_word(" ", 1), " "); + + // Entire word should be returned when there's only 1 word + assert_eq!(nth_word("s", 1), "s"); + assert_eq!(nth_word("hello", 1), "hello"); + + // Correct word should be returned irrespective of number of whitespaces + assert_eq!(nth_word("hello ", 1), "hello"); + assert_eq!(nth_word(" hello ", 1), "hello"); + assert_eq!(nth_word("\t \nhello", 1), "hello"); + + // Correct word should be returned when there are multiple words + assert_eq!(nth_word("so how are you?", 1), "so"); + assert_eq!(nth_word("so how are you?", 2), "how"); + assert_eq!(nth_word("so how are you?", 3), "are"); + assert_eq!(nth_word("so how are you?", 4), "you?"); + assert_eq!(nth_word(" hello how are ", 3), "are"); +} +// ANCHOR_END: usage \ No newline at end of file diff --git a/listings/ch04-understanding-ownership/no-listing-18-first-word-slice/src/main.rs b/listings/ch04-understanding-ownership/no-listing-18-first-word-slice/src/main.rs index f44a970daa..fe2acb58c6 100644 --- a/listings/ch04-understanding-ownership/no-listing-18-first-word-slice/src/main.rs +++ b/listings/ch04-understanding-ownership/no-listing-18-first-word-slice/src/main.rs @@ -1,10 +1,9 @@ // ANCHOR: here fn first_word(s: &String) -> &str { - let bytes = s.as_bytes(); - for (i, &item) in bytes.iter().enumerate() { - if item == b' ' { - return &s[0..i]; + for (i, ch) in s.char_indices() { + if ch.is_whitespace() { + return &s[..i]; } } diff --git a/listings/ch04-understanding-ownership/no-listing-19-slice-error/src/main.rs b/listings/ch04-understanding-ownership/no-listing-19-slice-error/src/main.rs index b23e45f435..22a37ceb4d 100644 --- a/listings/ch04-understanding-ownership/no-listing-19-slice-error/src/main.rs +++ b/listings/ch04-understanding-ownership/no-listing-19-slice-error/src/main.rs @@ -1,9 +1,8 @@ fn first_word(s: &String) -> &str { - let bytes = s.as_bytes(); - - for (i, &item) in bytes.iter().enumerate() { - if item == b' ' { - return &s[0..i]; + + for (i, ch) in s.char_indices() { + if ch.is_whitespace() { + return &s[..i]; } } diff --git a/src/ch04-03-slices.md b/src/ch04-03-slices.md index 7f145baf1d..0ea7dec4cd 100644 --- a/src/ch04-03-slices.md +++ b/src/ch04-03-slices.md @@ -5,15 +5,10 @@ _Slices_ let you reference a contiguous sequence of elements in a of reference, so it does not have ownership. Here’s a small programming problem: Write a function that takes a string of -words separated by spaces and returns the first word it finds in that string. -If the function doesn’t find a space in the string, the whole string must be +words separated by whitespaces and returns the first word it finds in that string. +If the function doesn’t find a whitespace in the string, the whole string must be one word, so the entire string should be returned. -> Note: For the purposes of introducing slices, we are assuming ASCII only in -> this section; a more thorough discussion of UTF-8 handling is in the -> [“Storing UTF-8 Encoded Text with Strings”][strings] section -> of Chapter 8. - Let’s work through how we’d write the signature of this function without using slices, to understand the problem that slices will solve: @@ -28,7 +23,7 @@ clear as we keep going.) But what should we return? We don’t really have a way to talk about *part* of a string. However, we could return the index of the end of the word, indicated by a space. Let’s try that, as shown in Listing 4-7. -