Chapter 1: Some basics
The chapter introduction previews the basics and explains that even simple Rust types involve choices, while the compiler strictly checks programs before they run.
After this chapter you will be able to
- Strict Compiler Checks. Explain what the introduction says about Rust’s strict compiler and why that strictness is useful.
- Rust’s benefits and learning curve. Explain what makes Rust appealing and why its different ideas take time to learn.
- Compiler checks before running. Explain why the section says Rust gives you the 'hangover first' and how the compiler helps.
- Compiler-guided code changes. Explain how the compiler makes contributing to and refactoring an existing Rust codebase less intimidating.
- Compile-time strictness and runtime errors. Explain why the section compares Rust to a critical but helpful spouse: Rust makes programmers address problems at compile time so fewer potential errors are left to discover at run time.
- Regular comment forms. Explain how to write comments with // and /* */, and what the compiler does with their contents.
- Documentation comment purpose. Distinguish /// documentation comments from ordinary // comments in purpose and audience.
- Integer types and ranges. Explain how signed and unsigned integer types differ, how their names indicate bit size, and why more bits allow larger values.
- Characters and numeric casts. Describe how Rust represents a char and how casting connects numeric and character types, including the risk of casting into u8.
- String bytes versus characters. Distinguish a string’s byte length from its character count and describe how the section obtains each.
- Rust Type Inference. Explain that Rust must know each variable’s type but can usually infer it without a type annotation, and that an unannotated integer such as 8 defaults to i32.
- Float forms and types. Describe how a decimal point makes a number a float in these examples, and distinguish Rust’s f32 and f64 types, including the default when no type is specified.
- Matching types in addition. Explain why adding an explicitly declared f32 to an f64 fails and how to read the resulting type-mismatch message.
- Resolving float type mismatches. Describe the two fixes shown for the mismatched float addition: casting with as or removing type declarations so Rust can infer types that work together.
- Functions and their arguments. Explain that `main()` starts the program, empty parentheses mean no arguments were passed, and a function that accepts variables names each argument and gives its type.
- Code blocks and variable lifetimes. Describe what a `{}` code block contains and how long a variable created inside it lives.
- Printing with brace placeholders. Distinguish braces in `println!` from a code block, and describe how they place variables into printed output.
- Function returns and semicolons. Explain how `-> i32` specifies a return type, why a final expression without a semicolon returns its value, and when an explicit `return` is useful.
- Variable declarations with let. Explain that Rust uses let to declare a variable and that a variable is a name representing information.
- Variable scope in blocks. Explain why a variable declared inside a code block cannot be printed after that block ends.
- Block values and semicolons. Explain how a block can supply a value to an outer variable, and what changes when a semicolon is added inside the block.
- Display and Debug formats. Distinguish {} Display printing from {:?} Debug printing and {:#?} pretty Debug printing. Explain when a reader might try each Debug format.
- Type minimum and maximum constants. Explain how to refer to a type’s smallest and largest numbers with its MIN and MAX constants, and why those names are capitalized.
- Default variable immutability. Explain that a variable declared with let alone cannot be changed.
- Mutable variable type limit. Describe how to declare a changeable variable with let mut and explain that mut does not allow its type to change.
- Shadowing versus mutability. Distinguish shadowing from mutability: shadowing uses let to declare a new variable with an existing name rather than changing the original variable.
- Visibility across scope blocks. Trace which same-named variable is visible when shadowing happens in one scope or inside a smaller block.
Section by section
Introduction
The chapter introduction previews the basics and explains that even simple Rust types involve choices, while the compiler strictly checks programs before they run.
- Choices in Simple Types: Explain why even simple Rust types, such as integers, can involve choices.
Why it matters: It prepares you to encounter more than one option when using a seemingly simple type. - Strict Compiler Checks: Explain what the introduction says about Rust’s strict compiler and why that strictness is useful.
Why it matters: Compiler rejection is presented as help, not just an obstacle.
Key terms: bits and bytes, simple types, integers, compiler, strict
1.1 Introducing Rust
Introduces Rust’s appeal and explains why its compiler makes learning feel demanding but helpful.
- Rust’s benefits and learning curve: Explain what makes Rust appealing and why its different ideas take time to learn.
Why it matters: These strengths explain both Rust’s popularity and why learning it takes some patience. - Compiler checks before running: Explain why the section says Rust gives you the 'hangover first' and how the compiler helps.
Why it matters: Compiler errors are part of how Rust helps you improve code before you run it.
Key terms: speed and control, memory safety, rich type system, compiler, hangover first, types, possible errors
1.1.1 A pep talk
The section reassures beginners that Rust’s difficult learning period gives way to confidence because the compiler helps identify problems when code changes.
- Rust’s changing learning curve: Explain why the author calls Rust fairly easy despite acknowledging that learning it can be frustrating.
Why it matters: Early frustration does not mean working in Rust will always feel that way. - Compiler-guided code changes: Explain how the compiler makes contributing to and refactoring an existing Rust codebase less intimidating.
Why it matters: Compiler feedback is the reason the author says even less-experienced developers can change existing Rust code with more confidence.
Key terms: frustrating days, doing a lot of the thinking for you, compile, compiler, contributing, refactoring
1.1.2 Rust is like a critical spouse
The section explains how Rust’s strict but helpful compiler catches problems before a program runs, guides corrections, and warns about unused code.
- Compile-time strictness and runtime errors: Explain why the section compares Rust to a critical but helpful spouse: Rust makes programmers address problems at compile time so fewer potential errors are left to discover at run time.
Why it matters: The compiler’s demands are intended to help you fix problems before running your program. - Compiler guidance through corrections: Describe how the example shows the compiler offering suggestions for incorrect code, then providing further feedback after the first corrections, until the code works.
Why it matters: A compiler message can be a useful guide to your next change, not just a rejection. - Unused code warnings: Distinguish a compiler warning about an unused variable from an error that prevents code from compiling and running.
Why it matters: Recognizing an unused-code warning helps you interpret compiler feedback without mistaking every message for a blocking error.
Key terms: compile time, run time, debug, Rust compiler, suggestions, warning, variable
1.2 Comments
This section explains how Rust comments are written and used, and briefly introduces named variables through an example.
- Regular comment forms: Explain how to write comments with // and /* */, and what the compiler does with their contents.
Why it matters: Comments help people understand code without changing what the compiler sees as code. - Documentation comment purpose: Distinguish /// documentation comments from ordinary // comments in purpose and audience.
Why it matters: The distinction helps you choose between a note for code readers and an explanation intended for documentation readers. - Named variable example: Describe what let some_number = 100; does and why a variable's name might change with its context.
Why it matters: A meaningful variable name makes it easier to remember what a value represents.
Key terms: comment, //, /* */, compiler, doc comment, documentation comment, ///, variable, let, some_number
1.3 Primitive types: Integers, characters, and strings
This section explains Rust’s basic integer and character types, how casts behave, and why a string’s byte length can differ from its character count.
- Integer types and ranges: Explain how signed and unsigned integer types differ, how their names indicate bit size, and why more bits allow larger values.
Why it matters: Choosing an integer type requires knowing whether values can be negative and how large they can get. - Binary bits and capacity: Trace how an eight-bit unsigned number represents a value and why the largest u8 is 255.
Why it matters: Binary place values make integer size limits less arbitrary. - Architecture sized indexing types: Explain how isize and usize depend on computer architecture and why Rust uses usize for indexing.
Why it matters: This explains why an index uses usize rather than an integer type with a fixed bit count. - Characters and numeric casts: Describe how Rust represents a char and how casting connects numeric and character types, including the risk of casting into u8.
Why it matters: It clarifies both what a char holds and why a seemingly convenient cast can change a number unexpectedly. - String bytes versus characters: Distinguish a string’s byte length from its character count and describe how the section obtains each.
Why it matters: A string can contain fewer characters than its byte length suggests.
Key terms: integer, signed integer, unsigned integer, bits, i8, u8, binary, bit, isize, usize
1.4 Type inference
Explains how Rust infers variable types and how numeric types and literals can be written explicitly.
- Rust Type Inference: Explain that Rust must know each variable’s type but can usually infer it without a type annotation, and that an unannotated integer such as 8 defaults to i32.
Why it matters: This explains why many Rust variable declarations need no written type. - Explicit Numeric Types: Describe when you might specify a type yourself and how to do so with a colon after the variable name or, for a number, a type after the number.
Why it matters: Explicit types let you choose a type other than the one Rust would infer. - Underscores In Numbers: Explain that underscores can make numeric literals easier to read without changing their value.
Why it matters: You can read grouped digits without treating the underscores as part of the value. - Decimal Points And Floats: Distinguish a whole-number integer from a number with a decimal point, which Rust treats as a float.
Why it matters: A decimal point changes the kind of number Rust sees.
Key terms: type inference, compiler, i32, type, i128, _, integer, float
1.5 Floats
Introduces Rust’s floating-point types, explains why mixing f32 and f64 causes a type error, and shows two ways to make the types compatible.
- Float forms and types: Describe how a decimal point makes a number a float in these examples, and distinguish Rust’s f32 and f64 types, including the default when no type is specified.
Why it matters: Recognizing float values and their default type helps you understand what Rust infers from a number. - Matching types in addition: Explain why adding an explicitly declared f32 to an f64 fails and how to read the resulting type-mismatch message.
Why it matters: The error tells you which type the addition needed and which type your code supplied. - Resolving float type mismatches: Describe the two fixes shown for the mismatched float addition: casting with as or removing type declarations so Rust can infer types that work together.
Why it matters: You can either make the types match explicitly or let Rust infer compatible float types.
Key terms: float, f32, f64, bits, expected (type), found (type), cast, as, type declarations
1.6 “Hello, World!” and printing
This section uses a Hello, World! program to explain basic function syntax, code blocks, printing, return values, and when Rust frees variable memory.
- Functions and their arguments: Explain that `main()` starts the program, empty parentheses mean no arguments were passed, and a function that accepts variables names each argument and gives its type.
Why it matters: This lets you read a function’s name and inputs before looking at its body. - Code blocks and variable lifetimes: Describe what a `{}` code block contains and how long a variable created inside it lives.
Why it matters: Block boundaries help explain when a local variable stops being available. - Printing with brace placeholders: Distinguish braces in `println!` from a code block, and describe how they place variables into printed output.
Why it matters: The same braces have different jobs in a function body and in printed text. - Function returns and semicolons: Explain how `-> i32` specifies a return type, why a final expression without a semicolon returns its value, and when an explicit `return` is useful.
Why it matters: A single semicolon can change a function’s result and cause a return-type error. - Automatic variable memory cleanup: Describe the section’s distinction between Rust freeing memory when variables are no longer needed, garbage collection, and manual cleanup.
Why it matters: This introduces why variable lifetimes matter for Rust’s memory use.
Key terms: fn, main(), argument, type, code block, lifetime, {}, println!, semicolon, ->
1.7 Declaring variables and code blocks
The section introduces variable declarations, two ways to print variables, and how code blocks affect variable scope and returned values.
- Variable declarations with let: Explain that Rust uses let to declare a variable and that a variable is a name representing information.
Why it matters: This is the starting point for reading the block examples in the section. - Two variable printing forms: Describe the two printing forms mentioned here: putting a variable name inside println! braces or supplying it after a comma following the braces.
Why it matters: Recognizing both forms helps you follow the book’s printing examples. - Variable scope in blocks: Explain why a variable declared inside a code block cannot be printed after that block ends.
Why it matters: Block boundaries explain why an apparently declared name can still be unavailable where you try to use it. - Block values and semicolons: Explain how a block can supply a value to an outer variable, and what changes when a semicolon is added inside the block.
Why it matters: A block’s returned value can be used outside the block even though its local variable cannot.
Key terms: let, variable, println!, {}, code block, lifetime, my_number, return value, semicolon, ()
1.8 Display and Debug
Explains Rust’s Display and Debug printing formats, how compiler errors can point to the right format, and how print! differs from println!.
- Display and Debug formats: Distinguish {} Display printing from {:?} Debug printing and {:#?} pretty Debug printing. Explain when a reader might try each Debug format.
Why it matters: Choosing a format lets you print types that do not support Display and read detailed output more easily. - Compiler printing error: Explain what a compiler message saying the Display trait is not implemented means in this section, and how it helps choose a printing format.
Why it matters: The compiler’s message can tell you why {} failed and suggest a format to try. - Printing without a newline: Distinguish print! from println! in terms of whether printing adds a new line.
Why it matters: This choice controls whether the next output continues on the same line.
Key terms: Display, Debug, pretty printing, {}, {:?}, {:#?}, trait, compiler, print!, println!
1.9 Smallest and largest numbers
This section shows how to refer to a numeric type’s smallest and largest values using MIN and MAX.
- Type minimum and maximum constants: Explain how to refer to a type’s smallest and largest numbers with its MIN and MAX constants, and why those names are capitalized.
Why it matters: You can recognize how Rust names and accesses a type’s numeric limits.
Key terms: MIN, MAX, consts, ::
1.10 Mutability (changing)
This section explains that Rust variables are immutable by default and that mut permits changing a value but not its type.
- Default variable immutability: Explain that a variable declared with let alone cannot be changed.
Why it matters: This explains why Rust rejects an attempted change to an ordinary variable. - Mutable variable type limit: Describe how to declare a changeable variable with let mut and explain that mut does not allow its type to change.
Why it matters: It separates permission to change a value from permission to change its type.
Key terms: let, immutable, let mut, mut, type
1.11 Shadowing
This section explains how Rust shadowing creates a new variable with an existing name, how scope affects which variable is visible, and why shadowing can be useful.
- Shadowing versus mutability: Distinguish shadowing from mutability: shadowing uses let to declare a new variable with an existing name rather than changing the original variable.
Why it matters: This distinction helps you tell whether code creates a new binding or changes an existing variable. - Visibility across scope blocks: Trace which same-named variable is visible when shadowing happens in one scope or inside a smaller block.
Why it matters: Scope tells you which value a name refers to at each point in the code. - Practical uses of shadowing: Explain why repeated shadowing can be useful for successive calculations and for creating a same-named variable with a different type or mutability.
Why it matters: Reusing a name can keep successive steps simple without inventing names for values you no longer need.
Key terms: shadowing, let binding, mutability, scope block, mutable variable, float
1.12 Summary
Where this is used later
- Using Cargo and what Rust does while it compiles (chapter 18) · Provides context for what Rust does while it compiles.
- Module basics (chapter 14) · Recognizing comments helps when reading code examples in modules.
- cargo doc (chapter 18) · The distinction provides context for generating documentation with cargo doc.
- (chapter intro) (chapter 5) · Introduces concrete types.
- Arrays (chapter 3) · Provides context for indexes when working with arrays.
- Casting enums into integers (chapter 4) · Builds on the idea of casting between types.
- Strings (chapter 2) · Provides the initial distinction between string contents and their byte length.
- Iterators (chapter 8) · Revisits methods for working through collections of characters.
Concepts explained in this chapter
- RustRust is a compiled systems programming language emphasizing memory safety, performance, and explicit control over low-level data representation.
- println! macroThe println! macro formats its arguments and writes the resulting text followed by a newline to standard output.
- integerAn integer is a whole-number numeric value without a fractional part.
- primitive data typeA primitive type is a built-in Rust type representing a basic value such as a number or Boolean.
- binary representationBinary representation expresses values using a base-two numeral system and corresponding bit patterns.
- unsigned integerAn unsigned integer type represents nonnegative whole numbers without a sign.
- UnicodeUnicode is a character encoding standard that assigns code points to characters across writing systems and symbols.
- as operatorThe as operator performs explicit primitive-type conversions in Rust.
- bitA bit is a binary digit representing one of two possible values in digital computation.
- bytesA byte is a fixed-size unit of digital information consisting of eight bits in conventional modern systems.
- char(n)Rust's char type represents one Unicode scalar value and occupies four bytes.
- data conversionType casting explicitly converts a value from one Rust type to another compatible type.
- indexingIndexing selects elements in an ordered collection by their numeric positions.
- pointer-sized integerA pointer-sized integer is a Rust signed or unsigned integer whose width matches the computer architecture.
- stringA string is a programming-language value representing an ordered sequence of characters or character units as text.
- compilerA compiler is a program that translates source code into an executable or another lower-level representation while checking it against the language's rules.
- bindingA variable is a named program binding that refers to a value and may permit reassignment according to its declaration.
- blockA code block is a delimited sequence of Rust statements and expressions that forms a lexical scope and produces a value.
- int32An int32 is a signed integer type represented using exactly 32 bits.
- lifetimeA variable lifetime is the interval during which a variable binding is valid and accessible.
- semicolonA semicolon is Rust syntax that terminates an expression statement and changes the value behavior of a block expression.
- compile timeCompile time is the phase before program execution in which source code is analyzed and transformed into executable code.
- shadowingShadowing is the Rust language feature that creates a new variable with the same name as an existing variable, hiding the earlier binding within the current scope.
- command-line argumentA command-line argument is a value supplied to a program when it is launched through a command-line interface.
- entry pointThe main function is the entry-point function that Rust starts when running an executable program.
- functionA function is a named unit of executable code that can accept inputs and produce a result.
- return typeA return type is the type of value that a function produces when it completes.
- unit typeA unit type is a type with exactly one value, commonly written (), that represents the absence of meaningful information or a meaningful result.
- commentA comment is source text that the compiler ignores as program instructions and that can contain explanatory or documentation text.
- DisplayDisplay printing formats a Rust value using its user-facing Display formatting behavior.
- DebugDebug is the Rust trait for producing programmer-oriented representations of values.
- traitA Rust trait is an abstract interface that defines capabilities or behavior a type can provide.
- printPrinting is the operation of writing textual or formatted data to an output stream.
- const qualifierA language feature that marks a value, binding, or access path as constant, restricting reassignment or modification according to the language’s rules.
- macroA Rust macro generates or transforms source code during compilation.
- documentationDocumentation is explanatory material that describes how code works and how it can be used.
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.
