Chapter 20: A tour of the standard library
Introduces a brief tour of the Rust standard library and previews the types and features the next sections will cover.
After this chapter you will be able to
- Arrays in for loops. Explain that before Rust 1.53, a for loop needed something like .iter() or a slice reference to iterate over an array, but modern Rust can use the array directly.
- Array destructuring patterns. Explain how to bind variables to array elements using names inside square brackets, including how destructuring can pull out the first and last elements.
- Array mapping results. Describe what an array's .map() method returns, how it differs from iterator mapping here, and what happens to the original array.
- Array construction with from_fn. Explain that `from_fn()` constructs an array by calling a closure for each position, passing its index to the closure. Describe when the closure can ignore that index.
- Array length type inference. Explain why `from_fn()` needs enough context to determine the array length, and how an array comparison or an explicit array type can provide it.
- Fallible numeric character conversion. Distinguish making a char from a u8 with From from trying to make one from a u32 with TryFrom, and explain why the latter can fail.
- Character versus string byte length. Explain the distinction between a char’s four-byte size and the number of bytes that character would use as a &str, which the section checks with len_utf().
- Checked integer arithmetic. Explain when to use the integer methods .checked_add(), .checked_sub(), .checked_mul(), and .checked_div(), and how their Option return values let code check arithmetic without overflowing or panicking.
- Overflow across compilation modes. Distinguish what happens when the compiler can identify integer overflow at compile time from what happens when a value is not known until runtime in Debug and Release modes.
- Custom addition with Add. Explain how implementing Add lets a custom type use the + operator and why its addition behavior must be supplied manually.
- Float whole-number methods. Distinguish what .floor(), .ceil(), .round(), and .trunc() do to an f32 or f64 value, and state that their results remain floats.
- Float extrema with fold. Explain how float .max() and .min() compare two values and how an iterator's .fold() can use them to find the highest or lowest value in a Vec or other iterable collection.
- Associated items and double colons. Explain that Rust has three kinds of associated items, that they are connected to a type or trait with `::`, and that functions are one kind.
- Methods as associated functions. Explain how a method with a self parameter can be called with dot syntax or with a type-qualified function call.
- Trait associated type definitions. Explain that an associated type is defined when a trait is implemented, using `Output` in an `Add` implementation as the section’s example. Describe how an implementer of the example transformation trait chooses what type the original type turns into.
- Type-associated constant definition. Describe how to declare an associated const in an impl block and how the examples use it to provide a value associated with a type, including a length of 10 for a string type.
- Boolean to integer conversion. Explain that Rust allows a bool to be converted to an integer, with true becoming 1 and false becoming 0, but does not allow the reverse conversion. Describe the type information needed when using .into().
- Conditional Option from bool. Describe how .then() turns a bool into an Option by calling a closure only when the bool is true and wrapping its return value. State that .then_some() is also introduced as a bool-to-Option method, without assuming its further mechanics.
- Stable and unstable sorting. Distinguish .sort() from .sort_unstable(): both sort a vector, but the unstable version may change the relative order of equal items and is usually faster.
- Adjacent duplicate removal. Explain why .dedup() removes only neighboring equal items and how sorting first can make it remove every duplicate value.
- Draining a vector range. Trace what .drain() provides for a range of Vec values and why collecting or dropping its iterator lets you access the original Vec again.
- String capacity and reallocations. Explain how String::with_capacity() can avoid repeated reallocations when building a String with push() or push_str().
- Operating-system string compatibility. Explain why Rust provides OsString and identify where it fits among the string-like types in std::ffi.
- OsString conversion failure. Describe what OsString::into_string() returns and how to retain the OsString if conversion fails.
What it builds on
- Destructuring (chapter 4) · Earlier destructuring of tuples and structs
- Mapping and filtering (chapter 9) · Earlier use of mapping with iterators
- Closures and closures inside iterators (chapter 8) · Closures as functions supplied to other code
- Type inference (chapter 1) · Type inference
- Arrays (chapter 3) · Arrays have a fixed length as part of their type
- Primitive types: Integers, characters, and strings (chapter 1) · The char type.
- The From trait (chapter 7) · The From trait.
- Strings (chapter 2) · Strings and &str.
Section by section
Introduction
Introduces a brief tour of the Rust standard library and previews the types and features the next sections will cover.
20.1 Arrays
Introduces upcoming changes that have made Rust arrays easier to work with.
20.1.1 Arrays now implement Iterator
This subsection explains that modern Rust can use arrays directly in for loops, unlike versions before Rust 1.53.
- Arrays in for loops: Explain that before Rust 1.53, a for loop needed something like .iter() or a slice reference to iterate over an array, but modern Rust can use the array directly.
Why it matters: It helps you recognize when an older tutorial describes a restriction that no longer applies.
Key terms: arrays, Iterator, .iter(), slice, for loops
20.1.2 Destructuring and mapping arrays
This section shows how to extract values from arrays by destructuring and transform arrays with .map().
- Array destructuring patterns: Explain how to bind variables to array elements using names inside square brackets, including how destructuring can pull out the first and last elements.
Why it matters: Destructuring lets you name the array values you want to use. - Array mapping results: Describe what an array's .map() method returns, how it differs from iterator mapping here, and what happens to the original array.
Why it matters: This lets you transform an array while keeping its length and without collecting an iterator.
Key terms: destructuring, array, [], .map(), .collect()
20.1.3 Using from_fn to make arrays
Explains how `from_fn()` constructs an array with a closure and how the compiler determines the array’s length.
- Array construction with from_fn: Explain that `from_fn()` constructs an array by calling a closure for each position, passing its index to the closure. Describe when the closure can ignore that index.
Why it matters: This lets you make array values from their positions without writing out every item. - Array length type inference: Explain why `from_fn()` needs enough context to determine the array length, and how an array comparison or an explicit array type can provide it.
Why it matters: Knowing where the length comes from helps you fix a `from_fn()` call that cannot compile.
Key terms: from_fn(), closure, index, |_|, type inference, array length, assert_eq!, [&str; 5], [_; 5]
20.2 char
This section shows several useful char methods and explains why converting numbers to characters can fail.
- Unicode escape for characters: Describe how .escape_unicode() displays a char as a Unicode escape, such as \u{ccad}.
Why it matters: It gives you a way to inspect a character’s Unicode number. - Fallible numeric character conversion: Distinguish making a char from a u8 with From from trying to make one from a u32 with TryFrom, and explain why the latter can fail.
Why it matters: The conversion you choose must account for whether the number can represent a character. - Character versus string byte length: Explain the distinction between a char’s four-byte size and the number of bytes that character would use as a &str, which the section checks with len_utf().
Why it matters: A char’s size does not by itself tell you its byte length in a string. - Character convenience methods: Describe the kinds of character checks and changes available through convenience methods, using an example from the section.
Why it matters: An existing char method may handle a simple check or change you need.
Key terms: char, .escape_unicode(), Unicode, u8, u32, From, TryFrom, &str, len_utf(), convenience methods
20.3 Integers
Introduces the upcoming discussion of practical integer methods while noting that many mathematical methods are outside its scope.
20.3.1 Checked operations
This section explains how checked integer operations avoid overflow and panics, and why ordinary overflow behaves differently at compile time, in Debug mode, and in Release mode.
- Checked integer arithmetic: Explain when to use the integer methods .checked_add(), .checked_sub(), .checked_mul(), and .checked_div(), and how their Option return values let code check arithmetic without overflowing or panicking.
Why it matters: Checked operations help prevent arithmetic surprises that can cause crashes or security problems. - Overflow across compilation modes: Distinguish what happens when the compiler can identify integer overflow at compile time from what happens when a value is not known until runtime in Debug and Release modes.
Why it matters: Compilation alone does not guarantee that ordinary integer arithmetic will avoid overflow at runtime.
Key terms: .checked_add(), .checked_sub(), .checked_mul(), .checked_div(), Option, overflow, compile time, Debug mode, Release mode
20.3.2 The Add trait and other similar traits
This section explains how traits let custom Rust types use arithmetic and comparison operators, and clarifies the term rhs in integer-method documentation.
- Right-hand-side terminology: Explain what rhs means in an integer method’s documentation and identify it in a simple expression such as 5 + 6.
Why it matters: Recognizing rhs makes method descriptions easier to read. - Custom addition with Add: Explain how implementing Add lets a custom type use the + operator and why its addition behavior must be supplied manually.
Why it matters: It connects a familiar operator to the trait implementation that gives it meaning for your own type. - Other arithmetic operator traits: Describe how other arithmetic operators, including assignment forms, have corresponding traits named in the standard library.
Why it matters: The naming pattern helps you find the trait associated with an arithmetic operator. - Equality and ordering traits: Explain what PartialEq and PartialOrd enable for a custom type and how their implementations differ from the Add example.
Why it matters: It shows that operator traits cover comparisons as well as arithmetic, but need not all be implemented the same way.
Key terms: rhs, right-hand side, Add, trait, +, #[derive(Add)], Sub, Mul, Div, AddAssign
20.4 Floats
Introduces float methods for producing whole-number-valued results and finding high or low values, including across an iterator.
- Float whole-number methods: Distinguish what .floor(), .ceil(), .round(), and .trunc() do to an f32 or f64 value, and state that their results remain floats.
Why it matters: The methods can produce similar-looking results, so it helps to keep their different rules straight. - Float extrema with fold: Explain how float .max() and .min() compare two values and how an iterator's .fold() can use them to find the highest or lowest value in a Vec or other iterable collection.
Why it matters: It shows that folding an iterator can find an extreme value, not just calculate a sum.
Key terms: f32, f64, .floor(), .ceil(), .round(), .trunc(), .max(), .min(), .fold(), Iterator
20.5 Associated items and associated constants
Introduces associated items as items tied to a type or trait with `::`, noting that functions are one of three kinds.
- Associated items and double colons: Explain that Rust has three kinds of associated items, that they are connected to a type or trait with `::`, and that functions are one kind.
Why it matters: This gives you a way to recognize how an item is connected to a type or trait.
Key terms: associated items, type, trait, :: double colon, functions
20.5.1 Associated functions
This section explains that methods are associated functions and that dot syntax is a convenient way to call them when they have a self parameter.
- Methods as associated functions: Explain how a method with a self parameter can be called with dot syntax or with a type-qualified function call.
Why it matters: It helps you recognize what a dot-method call is doing when you encounter the type-qualified form.
Key terms: associated function, self parameter, dot operator, :: syntax
20.5.2 Associated types
This section explains that a trait implementation defines its associated type and that the type can be referred to with a `::` signature.
- Trait associated type definitions: Explain that an associated type is defined when a trait is implemented, using `Output` in an `Add` implementation as the section’s example. Describe how an implementer of the example transformation trait chooses what type the original type turns into.
Why it matters: It helps you see where a trait’s resulting type comes from. - Associated type signatures: Describe how the section refers to an associated type through the implementing type with `::`, including its use of a full associated type signature for the addition example.
Why it matters: It helps you recognize what a full associated type signature refers to when reading Rust code.
Key terms: associated type, trait, Add trait, Output, associated type signature, :: double colon
20.5.3 Associated consts
This section shows how to define an associated const on a type and how a type can override an associated const supplied by a trait.
- Type-associated constant definition: Describe how to declare an associated const in an impl block and how the examples use it to provide a value associated with a type, including a length of 10 for a string type.
Why it matters: A constant associated with a type can provide a value that code using the type needs. - Trait constant overrides: Explain that a trait can have an associated const and that a type can override its default value; describe the change in the example’s printed output.
Why it matters: A type can use a trait’s default constant or supply its own value.
Key terms: associated const, Impl block, const, trait, override
20.6 bool
This section shows how Rust converts bool values to integers and uses bool methods to produce Option values.
- Boolean to integer conversion: Explain that Rust allows a bool to be converted to an integer, with true becoming 1 and false becoming 0, but does not allow the reverse conversion. Describe the type information needed when using .into().
Why it matters: This lets you predict which boolean conversions Rust accepts and what values they produce. - Conditional Option from bool: Describe how .then() turns a bool into an Option by calling a closure only when the bool is true and wrapping its return value. State that .then_some() is also introduced as a bool-to-Option method, without assuming its further mechanics.
Why it matters: This gives you a concise way to make an optional value depend on a boolean condition.
Key terms: bool, .into(), Option, .then(), .then_some(), closure
20.7 Vec
Introduces several Vec methods for sorting, removing adjacent duplicates, splitting into slices, and draining a range.
- Stable and unstable sorting: Distinguish .sort() from .sort_unstable(): both sort a vector, but the unstable version may change the relative order of equal items and is usually faster.
Why it matters: The distinction helps you choose whether the order of equal items matters. - Adjacent duplicate removal: Explain why .dedup() removes only neighboring equal items and how sorting first can make it remove every duplicate value.
Why it matters: Sorting first prevents separated duplicates from being left behind. - Vector splitting into slices: Describe how .split_at() and .split_at_mut() give two slices of a Vec, and when to use the mutable version.
Why it matters: You can work with two parts of a vector without consuming the vector. - Draining a vector range: Trace what .drain() provides for a range of Vec values and why collecting or dropping its iterator lets you access the original Vec again.
Why it matters: The iterator’s borrow explains when you can use the original vector again.
Key terms: Vec, .sort(), .sort_unstable(), stable sorting, .dedup(), .split_at(), .split_at_mut(), slices, .drain(), iterator
20.8 String
This section shows how String shares several methods with Vec and how to manage its capacity and characters.
- String capacity and reallocations: Explain how String::with_capacity() can avoid repeated reallocations when building a String with push() or push_str().
Why it matters: Choosing capacity up front can save repeated copying when you expect a String to grow. - Shrinking excess String capacity: Explain why and when to use shrink_to_fit() on a String after it has grown.
Why it matters: Shrinking too early can undo the benefit of keeping room for more text. - String character retention: Describe how String::retain() uses a closure to decide which characters remain in a String.
Why it matters: It provides a direct way to remove unwanted characters from a String. - String pop method: State that String supports pop(), a method also available on Vec.
Why it matters: It is another familiar Vec method you can use with String.
Key terms: String::with_capacity(), capacity, reallocate, push(), push_str(), length, shrink_to_fit(), retain(), closure, filter()
20.9 OsString and CString
Introduces std::ffi string types, explains why OsString is useful for operating-system strings, and shows how conversion to String handles failure.
- Operating-system string compatibility: Explain why Rust provides OsString and identify where it fits among the string-like types in std::ffi.
Why it matters: It explains why an operating-system string is not always interchangeable with a Rust String. - OsString conversion failure: Describe what OsString::into_string() returns and how to retain the OsString if conversion fails.
Why it matters: A failed conversion need not discard the original operating-system string.
Key terms: std::ffi, OsString, CString, OsStr, CStr, String, OsString::into_string(), Result, Err, unwrap
20.10 Summary
Concepts explained in this chapter
- associated constantAn associated constant is a constant value defined within a Rust type or trait and accessed through that type or trait.
- add traitThe Rust Add trait defines how a type participates in addition with another value.
- arrayAn array is a fixed-size Rust collection whose length is known at compile time and whose elements have one type.
- TryFromTryFrom is a Rust trait for fallible conversion between types that may fail for some input values.
- char(n)Rust's char type represents one Unicode scalar value and occupies four bytes.
- stringA string is a programming-language value representing an ordered sequence of characters or character units as text.
- data transformationmap is an iterator adaptor that applies a transformation to each item and returns a new iterator producing the transformed items.
- associated typeAn associated type is a type declaration attached to a Rust trait whose concrete value is specified for each implementation.
- iteratorAn iterator is a Rust value implementing the Iterator trait that produces a sequence of items, typically through repeated calls to next.
- slicingA slice is a dynamically sized borrowed view of a contiguous sequence of values.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
