From 0a3e4da3bf8958380c9bad2d8e88c1f9059cbe80 Mon Sep 17 00:00:00 2001 From: mygithubid1 Date: Sat, 4 Jul 2026 23:02:37 +0530 Subject: [PATCH 1/2] Replace usage of `bytes` to characters to make the code and writeup UTF-8 friendly. --- .../listing-04-07/src/main.rs | 11 ++-- .../listing-04-08/src/main.rs | 6 +- .../listing-04-09/src/main.rs | 7 +-- .../listing-04-10/Cargo.lock | 6 ++ .../listing-04-10/Cargo.toml | 6 ++ .../listing-04-10/src/main.rs | 62 +++++++++++++++++++ .../src/main.rs | 7 +-- .../no-listing-19-slice-error/src/main.rs | 9 ++- src/ch04-03-slices.md | 58 ++++++++--------- 9 files changed, 120 insertions(+), 52 deletions(-) create mode 100755 listings/ch04-understanding-ownership/listing-04-10/Cargo.lock create mode 100755 listings/ch04-understanding-ownership/listing-04-10/Cargo.toml create mode 100755 listings/ch04-understanding-ownership/listing-04-10/src/main.rs 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. -+ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:here}} @@ -36,42 +31,33 @@ of the word, indicated by a space. Let’s try that, as shown in Listing 4-7. -Because we need to go through the `String` element by element and check whether -a value is a space, we’ll convert our `String` to an array of bytes using the -`as_bytes` method. +Because we need to go through the `String` one character at a time and check whether +the character is a whitespace, we’ll iterator over our `String` using `char_indices` method. ```rust,ignore -{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:as_bytes}} +{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:char_indices}} ``` -Next, we create an iterator over the array of bytes using the `iter` method: +Next, iterate over the `(character, index)` tuples using `char_indices`: ```rust,ignore {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:iter}} ``` We’ll discuss iterators in more detail in [Chapter 13][ch13]. -For now, know that `iter` is a method that returns each element in a collection -and that `enumerate` wraps the result of `iter` and returns each element as -part of a tuple instead. The first element of the tuple returned from -`enumerate` is the index, and the second element is a reference to the element. -This is a bit more convenient than calculating the index ourselves. - -Because the `enumerate` method returns a tuple, we can use patterns to -destructure that tuple. We’ll be discussing patterns more in [Chapter -6][ch6]. In the `for` loop, we specify a pattern that has `i` -for the index in the tuple and `&item` for the single byte in the tuple. -Because we get a reference to the element from `.iter().enumerate()`, we use -`&` in the pattern. - -Inside the `for` loop, we search for the byte that represents the space by -using the byte literal syntax. If we find a space, we return the position. -Otherwise, we return the length of the string by using `s.len()`. +For now, know that `char_indices` is an `iterator` over each character in the string and its position. The first element of the tuple returned from `iterator` is the index, and the second element is the character. This is a bit more convenient than calculating the index ourselves. + +Since a tuple is returned, we can use patterns to destructure that tuple. We’ll be discussing patterns more in [Chapter 6][ch6]. In the `for` loop, we specify a pattern that has `i` for the index in the tuple and `ch` for the character in the tuple. + +Inside the `for` loop, we search for the char that represents the whitespace by +using `is_whitespace` method. If we find a whitespace, we return the end of first word. +Otherwise, we return the string length as end of first word. ```rust,ignore {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:inside_for}} ``` + We now have a way to find out the index of the end of the first word in the string, but there’s a problem. We’re returning a `usize` on its own, but it’s only a meaningful number in the context of the `&String`. In other words, @@ -291,6 +277,20 @@ makes our API more general and useful without losing any functionality: +#### Solution to getting the Nth word + +Here's a solution to get the Nth word within a string slice accounting for multiple whitespaces and leading whitespaces: + ++ +```rust +{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-10/src/main.rs:here}} +``` + + + + + ### Other Slices String slices, as you might imagine, are specific to strings. But there’s a From 217e03c1283c348b2acda64aa71b6751ba3d9ba7 Mon Sep 17 00:00:00 2001 From: mygithubid1 Date: Sat, 4 Jul 2026 23:20:01 +0530 Subject: [PATCH 2/2] add the word whitespaces (plural form) to the dictionary --- ci/dictionary.txt | 1 + 1 file changed, 1 insertion(+) 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