Learn Rust in a Month of Lunches
()
About this ebook
Learn Rust in a Month of Lunches teaches you to write super fast and super safe Rust code through lessons you can fit in your lunch break. Crystal-clear explanations and focused, relevant examples make it accessible to anyone—even if you’re learning Rust as your first programming language.
By the time you’re done reading Learn Rust in a Month of Lunches you’ll be able to:
- Build real software in Rust
- Understand messages from the compiler and Clippy, Rust’s coding coach
- Make informed decisions on the right types to use in any context
- Make sense of the Rust standard library and its commonly used items
- Use external Rust “crates” (libraries) for common tasks
- Comment and build documentation for your Rust code
- Work with crates that use async Rust
- Write simple declarative macros
- Explore test driven development in Rust
Learn Rust in a Month of Lunches is full of 24 easy-to-digest lessons that ease you into real Rust programming. You’ll learn essential Rust skills you can use for everything from system programming, to web applications, and games. By the time you’re done learning, you’ll know exactly what makes Rust unique—and be one of the thousands of developers who say it’s their best loved language!
About the technology
Learn how to create fast powerful programs in Rust in just 24 short lessons! Rust gives you modern features like a top-notch compiler, a rich ecosystem of pre-built libraries, and the same low-level performance you get with a language like C, but without the awkward syntax, complex memory management, and code safety concerns. This book guides you step by step from your first line of code.
About the book
Learn Rust in a Month of Lunches breaks down the Rust language into concise hands-on lessons designed to be completed in an hour or less. The examples are fun and easy to follow, so you’ll quickly progress from zero Rust knowledge to handling async and writing your own macros. You won’t even need to install Rust—the book’s code samples run in the browser-based Rust Playground. There’s no easier way to get started!
What's inside
- Build working Rust software
- Understand messages from the compiler and Clippy
- Use external Rust “crates” (libraries) for common tasks
- Explore test driven development in Rust
About the reader
No previous experience with Rust required.
About the author
Dave MacLeod was an educator, Korean-English translator, project controller, and copywriter before becoming a full-time Rust developer. The technical editor on this book was Jerry Kuch.
Table of Contents
1 Some basics
2 Memory, variables, and ownership
3 More complex types
4 Building your own types
5 Generics, option, and result
6 More collections, more error handling
7 Traits: Making different types do the same thing
8 Iterators and closures
9 Iterators and closures again!
10 Lifetimes and interior mutability
11 Multiple threads and a lot more
12 More on closures, generics, and threads
13 Box and Rust documentation
14 Testing and building your code from tests
15 Default, the builder pattern, and Deref
16 Const, “unsafe” Rust, and external crates
17 Rust’s most popular crates
18 Rust on your computer
19 More crates and async Rust
20 A tour of the standard library
21 Continuing the tour
22 Writing your own macros
23 Unfinished projects: Projects for you to finish
24 Unfinished projects, continued
David MacLeod
Dave MacLeod has worked as an educator, Korean-English translator, project controller, and copywriter before becoming a full-time Rust developer.
Related to Learn Rust in a Month of Lunches
Related ebooks
Build an Orchestrator in Go (From Scratch) Rating: 0 out of 5 stars0 ratingsLearn C++ by Example: Covers versions 11 to 23 Rating: 0 out of 5 stars0 ratingsTiny C Projects Rating: 0 out of 5 stars0 ratingsRust Servers, Services, and Apps Rating: 0 out of 5 stars0 ratingsRust Web Development: With warp, tokio, and reqwest Rating: 0 out of 5 stars0 ratingsTroubleshooting Java: Read, debug, and optimize JVM applications Rating: 0 out of 5 stars0 ratingsDependency Injection: Design patterns using Spring and Guice Rating: 0 out of 5 stars0 ratingsRx.NET in Action Rating: 0 out of 5 stars0 ratingsThe Well-Grounded Java Developer: Vital techniques of Java 7 and polyglot programming Rating: 4 out of 5 stars4/5RabbitMQ in Action: Distributed Messaging for Everyone Rating: 4 out of 5 stars4/5Beginning Rust: From Novice to Professional Rating: 0 out of 5 stars0 ratingsNim in Action Rating: 0 out of 5 stars0 ratingsDSLs in Action Rating: 4 out of 5 stars4/5The Joy of Clojure Rating: 4 out of 5 stars4/5Parallel and High Performance Computing Rating: 0 out of 5 stars0 ratingsObject Design Style Guide Rating: 0 out of 5 stars0 ratingsThe Quick Python Book Rating: 0 out of 5 stars0 ratingsKotlin in Action, Second Edition Rating: 0 out of 5 stars0 ratingsClojure in Action Rating: 0 out of 5 stars0 ratingsRuby in Practice Rating: 0 out of 5 stars0 ratingsJulia as a Second Language Rating: 0 out of 5 stars0 ratingsHaskell in Depth Rating: 0 out of 5 stars0 ratingsObjective-C Fundamentals Rating: 0 out of 5 stars0 ratingsThe Mikado Method Rating: 0 out of 5 stars0 ratingsGet Programming with JavaScript Next: New features of ECMAScript 2015, 2016, and beyond Rating: 0 out of 5 stars0 ratingsHTML5 for .NET Developers: Single page web apps, JavaScript, and semantic markup Rating: 0 out of 5 stars0 ratingsFunctional Programming in Scala Rating: 4 out of 5 stars4/5Programming with Types: Examples in TypeScript Rating: 0 out of 5 stars0 ratingsCloud Observability in Action Rating: 0 out of 5 stars0 ratingsGrokking Data Structures Rating: 0 out of 5 stars0 ratings
Programming For You
Coding All-in-One For Dummies Rating: 4 out of 5 stars4/5Excel : The Ultimate Comprehensive Step-By-Step Guide to the Basics of Excel Programming: 1 Rating: 5 out of 5 stars5/5Learn to Code. Get a Job. The Ultimate Guide to Learning and Getting Hired as a Developer. Rating: 5 out of 5 stars5/5Python Programming : How to Code Python Fast In Just 24 Hours With 7 Simple Steps Rating: 4 out of 5 stars4/5SQL QuickStart Guide: The Simplified Beginner's Guide to Managing, Analyzing, and Manipulating Data With SQL Rating: 4 out of 5 stars4/5Excel 101: A Beginner's & Intermediate's Guide for Mastering the Quintessence of Microsoft Excel (2010-2019 & 365) in no time! Rating: 0 out of 5 stars0 ratingsPython Data Structures and Algorithms Rating: 5 out of 5 stars5/5Learn PowerShell in a Month of Lunches, Fourth Edition: Covers Windows, Linux, and macOS Rating: 5 out of 5 stars5/5PYTHON: Practical Python Programming For Beginners & Experts With Hands-on Project Rating: 5 out of 5 stars5/5HTML in 30 Pages Rating: 5 out of 5 stars5/5Learning JavaScript Data Structures and Algorithms Rating: 5 out of 5 stars5/5C# Programming from Zero to Proficiency (Beginner): C# from Zero to Proficiency, #2 Rating: 0 out of 5 stars0 ratingsPython QuickStart Guide: The Simplified Beginner's Guide to Python Programming Using Hands-On Projects and Real-World Applications Rating: 0 out of 5 stars0 ratingsLinux Command-Line Tips & Tricks Rating: 0 out of 5 stars0 ratingsSQL: For Beginners: Your Guide To Easily Learn SQL Programming in 7 Days Rating: 5 out of 5 stars5/5HTML & CSS: Learn the Fundaments in 7 Days Rating: 4 out of 5 stars4/5Linux: Learn in 24 Hours Rating: 5 out of 5 stars5/5Hacking: Ultimate Beginner's Guide for Computer Hacking in 2018 and Beyond: Hacking in 2018, #1 Rating: 4 out of 5 stars4/5The Most Concise Step-By-Step Guide To ChatGPT Ever Rating: 3 out of 5 stars3/5Coding All-in-One For Dummies Rating: 0 out of 5 stars0 ratings
Reviews for Learn Rust in a Month of Lunches
0 ratings0 reviews
Book preview
Learn Rust in a Month of Lunches - David MacLeod
inside front cover
Learn Rust in a Month of Lunches takes you from the basics ...
... to Rust’s more advanced concepts ...
... and finishes up with six fun projects, including a laser pointer for your pet!
Learn Rust in a Month of Lunches
Dave MacLeod
Foreword by Allen Wyma
To comment go to liveBook
Manning
Shelter Island
For more information on this and other Manning titles go to
www.manning.com
Copyright
For online information and ordering of these and other Manning books, please visit www.manning.com. The publisher offers discounts on these books when ordered in quantity.
For more information, please contact
Special Sales Department
Manning Publications Co.
20 Baldwin Road
PO Box 761
Shelter Island, NY 11964
Email: orders@manning.com
©2024 by Manning Publications Co. All rights reserved.
No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by means electronic, mechanical, photocopying, or otherwise, without prior written permission of the publisher.
Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in the book, and Manning Publications was aware of a trademark claim, the designations have been printed in initial caps or all caps.
♾ Recognizing the importance of preserving what has been written, it is Manning’s policy to have the books we publish printed on acid-free paper, and we exert our best efforts to that end. Recognizing also our responsibility to conserve the resources of our planet, Manning books are printed on paper that is at least 15 percent recycled and processed without the use of elemental chlorine.
ISBN: 9781633438231
dedication
To MB, Windy, Gomes, and Johnny and to the cloudtops of Venus, the most Earth-like environment in the solar system.
contents
Front matter
foreword
preface
acknowledgments
about this book
about the author
1 Some basics
1.1 Introducing Rust
A pep talk
Rust is like a critical spouse
1.2 Comments
1.3 Primitive types: Integers, characters, and strings
1.4 Type inference
1.5 Floats
1.6 Hello, World!
and printing
1.7 Declaring variables and code blocks
1.8 Display and Debug
1.9 Smallest and largest numbers
1.10 Mutability (changing)
1.11 Shadowing
2 Memory, variables, and ownership
2.1 The stack, the heap, pointers, and references
2.2 Strings
2.3 const and static
2.4 More on references
2.5 Mutable references
Rust’s reference rules
Situation 1: Only one mutable reference
Situation 2: Only immutable references
Situation 3: The problem situation
2.6 Shadowing again
2.7 Giving references to functions
2.8 Copy types
2.9 Variables without values
2.10 More about printing
3 More complex types
3.1 Collection types
Arrays
Vectors
Tuples
3.2 Control flow
Basic control flow
Match statements
Loops
4 Building your own types
4.1 A quick overview of structs and enums
Structs
Enums
Casting enums into integers
Enums to use multiple types
Implementing structs and enums
4.2 Destructuring
4.3 References and the dot operator
5 Generics, option, and result
5.1 Generics
5.2 Option and Result
Option
Result
Some other ways to do pattern matching
6 More collections, more error handling
6.1 Other collections
HashMap (and BTreeMap)
HashSet and BTreeSet
BinaryHeap
VecDeque
6.2 The ? operator
6.3 When panic and unwrap are good
7 Traits: Making different types do the same thing
7.1 Traits: The basics
All you need are the method signatures
More complex examples
Traits as bounds
Traits are like qualifications
7.2 The From trait
7.3 The orphan rule
7.4 Getting around the orphan rule with newtypes
7.5 Taking a String and a &str in a function
8 Iterators and closures
8.1 Chaining methods
8.2 Iterators
8.3 Closures and closures inside iterators
Closures inside of methods
Closures: Lazy and fast
|_| in a closure
9 Iterators and closures again!
9.1 Helpful methods for closures and iterators
Mapping and filtering
Some more iterator and related methods
Checking and finding items inside iterators
Cycling, zipping, folding, and more
9.2 The dbg! macro and .inspect
10 Lifetimes and interior mutability
10.1 Types of &str
10.2 Lifetime annotations
Lifetimes in functions
Lifetime annotations in types
The anonymous lifetime
10.3 Interior mutability
Cell
RefCell
Mutex
RwLock
11 Multiple threads and a lot more
11.1 Importing and renaming inside a function
11.2 The todo! macro
11.3 Type aliases
11.4 Cow
11.5 Rc
Why Rc exists
Using Rc in practice
Avoiding lifetime annotations with Rc
11.6 Multiple threads
Spawning threads
Using JoinHandles to wait for threads to finish
Types of closures
Using the move keyword
12 More on closures, generics, and threads
12.1 Closures as arguments
Some simple closures
The relationship between FnOnce, FnMut, and Fn
Closures are all unique
A closure example
12.2 impl Trait
Regular generics compared to impl Trait
Returning closures with impl Trait
12.3 Arc
12.4 Scoped threads
12.5 Channels
Channel basics
Implementing a channel
13 Box and Rust documentation
13.1 Reading Rust documentation
assert_eq!
Searching
The [src] button
Information on traits
Attributes
13.2 Box
Some Box basics
Putting a Box around traits
Using a Box to handle multiple error types
Downcasting to a concrete type
14 Testing and building your code from tests
14.1 Crates and modules
Module basics
More on how the pub keyword works
Modules inside modules
14.2 Testing
Just add #[test], and now it’s a test
What happens when tests fail
Writing multiple tests
14.3 Test-driven development
Building a calculator: Starting with the tests
Putting the calculator together
15 Default, the builder pattern, and Deref
15.1 Implementing Default
15.2 The builder pattern
Writing builder methods
Adding a final check to the builder pattern
Making the builder pattern more rigorous
15.3 Deref and DerefMut
Deref basics
Implementing Deref
Implementing DerefMut
Using Deref the wrong way
16 Const, unsafe
Rust, and external crates
16.1 Const generics
16.2 Const functions
16.3 Mutable statics
16.4 Unsafe Rust
Overview of unsafe Rust
Using static mut in unsafe Rust
Rust’s most famous unsafe method
Methods ending in _unchecked
16.5 Introducing external crates
Crates and Cargo.toml
Using the rand crate
Rolling some dice with rand
17 Rust’s most popular crates
17.1 serde
17.2 Time in the standard library
17.3 chrono
Checking the code inside external crates
Back to chrono
17.4 Rayon
17.5 Anyhow and thiserror
Anyhow
thiserror
17.6 Blanket trait implementations
17.7 lazy_static and once_cell
Lazy static: Lazily evaluated statics
OnceCell: A cell to only write to once
18 Rust on your computer
18.1 Cargo
Why everyone uses Cargo
Using Cargo and what Rust does while it compiles
18.2 Working with user input
User input through stdin
Accessing command-line arguments
Accessing environment variables
18.3 Using files
Creating files
Opening existing files
Using OpenOptions to work with files
18.4 cargo doc
19 More crates and async Rust
19.1 The reqwest crate
19.2 Feature flags
19.3 Async Rust
Async basics
Checking whether a Future is ready
Using an async run time
Some other details about async Rust
20 A tour of the standard library
20.1 Arrays
Arrays now implement Iterator
Destructuring and mapping arrays
Using from_fn to make arrays
20.2 char
20.3 Integers
Checked operations
The Add trait and other similar traits
20.4 Floats
20.5 Associated items and associated constants
Associated functions
Associated types
Associated consts
20.6 bool
20.7 Vec
20.8 String
20.9 OsString and CString
21 Continuing the tour
21.1 std::mem
21.2 Setting panic hooks
21.3 Viewing backtraces
21.4 The standard library prelude
21.5 Other macros
unreachable!
column!, line!, file!, and module_path!
thread_local!
cfg!
22 Writing your own macros
22.1 Why macros exist
22.2 Writing basic macros
22.3 Reading macros from the standard library
22.4 Using macros to keep your code clean
23 Unfinished projects: Projects for you to finish
23.1 Setup for the last two chapters
23.2 Typing tutor
Setup and first code
Developing the code
Further development and cleanup
Over to you
23.3 Wikipedia article summary searcher
Setup and first code
Developing the code
Further development and cleanup
Over to you
23.4 Terminal stopwatch and clock
Setup and first code
Developing the code
Further development and cleanup
Over to you
24 Unfinished projects, continued
24.1 Web server word-guessing game
Setup and first code
Developing the code
Further development and cleanup
Over to you
24.2 Laser pointer
Setup and first code
Developing the code
Further development and cleanup
Over to you
24.3 Directory and file navigator
Setup and first code
Developing the code
Further development and cleanup
Over to you
index
front matter
foreword
The time to learn Rust is now. The sheer amount of C/C++ code written over the past 40 years is mind-boggling. It is used in nearly every operating system and embedded system, even powering some of the most popular programming languages such as Python and JavaScript. It has long been a way to make libraries portable and usable in almost any platform from one to another, even with different CPU architectures. It has also been the source of most hacks and vulnerabilities.
You can think of C/C++ as a katana, but without a handle. You can easily craft what you want, but if you squeeze too tight or aren’t very careful, you can cut yourself or others. This has long been the tradeoff that we have made to get the run-time speed and portability that we require when creating software. Thanks to Rust, there’s no more need to balance risk and speed, as it addresses most of the safety issues that have long been coupled with traditional C/C++ development.
This book does an excellent job of easing potential Rustaceans into how Rust works in small, easily digestible pieces that any developer can consume and understand. This is the first time I’ve seen a book start off showing how to comment in the language, which is great because you’ll always want to make notes in your code so you can remember what it does! It’s stunning that some books don’t consider the fundamental needs of a beginner reader; I’m happy to report that this book does. I also enjoy that this book slowly creeps into more advanced topics such as shadowing, which will usually come up in a Rustacean’s career before a tutorial mentions it, but it is still important nonetheless.
Learning Rust isn’t a simple achievement. There’s definitely a rabbit hole of information to burrow through, and that path will change whether you’re writing operating systems or a simple web service. This book will give you the basics and ease you into Rust without requiring you to install it until toward the end of the book, which definitely can help you finish within a month of lunches.
You’ve done yourself a favor in buying this book; now do yourself another favor and read it cover to cover.
—Allen Wyma, host of Rustacean Station podcast, Director at Plangora Limited
preface
I’ll forever be grateful to Rust for solving a paradox in my life, that of having a software-developer-like mind without a place to apply it. I took to programming languages like a duck to water as a child in the 1980s, which at the time meant BASIC, which I liked, and Logo, which I didn’t. But truly getting into programming at the time didn’t mean taking your fancy laptop to Starbucks every day to interact with people across the globe; it meant spending days inside the computer lab at school with the blinds shut, typing away as the rest of the world went about its business in the sun. Without knowing that programming was much more than working on BASIC and Logo all day (I probably would have loved Ada if I had known about it), I didn’t get very far and eventually fell out of love with the idea of programming and moved on to other interests like heavy metal.
Other attempts decades later to learn a few popular programming languages never worked, as they were either too high level, hiding details that I was interested in and lacking in performance for what I really wanted to do (make video games), or too low level, lacking safeguards and outright intimidating. There were no external factors forcing me to learn to code either, as I was already an adult and working full time in other fields.
One day in 2019 I set myself to learn Python, Javascript, and other popular languages for the umpteenth time and thought that I might give this new language called Rust a look. I had heard that it was challenging and incredibly low level and that you needed to be a grizzled old software developer of decades to even hope to make heads or tails of it. Two days later, I was hooked, and my programming language wanderlust was gone. Without getting into too many details, suffice it to say that I had found the language that could make what I wanted to make and that showed me the low-level details I craved to see but with safeguards in place to avoid too many pitfalls.
Learning Rust was a phenomenal experience, and even in 2019, there were sufficient resources to do so. But I think the first encounter with Rust can be even further refined, and that’s where this book comes from. If this book ends up being the difference for enough people between giving up on Rust and going all the way, the years put into making it will have all been worth it.
acknowledgments
It takes so many people to put a book like this together. First, I’d like to thank all my family and friends without citing any names in particular, because all of them are fairly private individuals. None of them live nearby, or even on the same continent, but they are an inexhaustible bedrock of support regardless of the physical distance.
As far as years go, I’d like to thank 1999, 2002, and 2017–2018. And, on the subject of years, I’d like to thank Mary Ann Day-Nasr, Mark Lennox, and my other teammates for managing to make 2011 and subsequent years at least pretty OK. I’d also like to thank James Massey for officially turning me from a guy who learned Rust into a professional Rust developer by giving me my first job in the field using the language.
At Manning, I’d like to thank Andy Waldron, my acquisition editor, for seeing the potential of the book and making it happen in the first place; Ian Hough, development editor, for overseeing the entire process and being my main point of contact at Manning from start to finish; and Jerry Kuch, technical editor, for his insightful comments that read much in the same way that the book itself is meant to: an experienced yet friendly hand nudging you in the right direction. Other thanks go to Geert Van Laethem, my technical proofreader, who assisted with checking the code to make sure all the code samples worked as expected. Last, but not least, thanks to the entire Manning production staff who worked behind the scenes to assemble this book into its final form.
To all the reviewers—Adam Wendell, Al Norman, Al Pezewski, Alex Lucas, Alexey Vyskubov, Amit Lamba, Andreas Schroepfer, Antonio Gagliardi, Balasubramanian Sivasankaran, Bikalpa Timilsina, Dan Sheikh, David Jacobs, David White, Francisco Claude, Giovanni Alzetta, Giuseppe Catalano, Helmut Reiterer, Horaci Macias, Ionel Olteanu, Jane Noesgaard Larsen, Jean Lazarou, Jean-Baptiste Bang Nteme, Jeremy Gailor, Joel Kotarski, John Paraskevopoulos, Jonathan Camara, Jonathan Reeves, Karol Skork, Kent Spillner, Kyle Manning, Laud Bentil, Marcello la Rocca, Marcus Geselle, Marek Petak, Maxim Levkov, Michael Bright, Michael Wright, Mohsen Mostafa Jokar, Olivier Ducatteeuw, Rich Yonts, Richard Meinsen, Rohit Sharma, Rosalyn Williams, Sergio Britos, Seung-jin Kim, Si Dunn, Slavomir Furman, Srikar Vedantam, Thomas Lockney, Thomas Peklak, Tiklu Ganguly, Tim Clark, Tim van Deurzen, and William Wheeler—your suggestions helped make this a better book.
about this book
When Rust was released in 2015, it had to convince the world that it was worth learning. Back then, a lot of books compared Rust to languages like C++ and C because Rust is a good alternative language for C++ and C programmers. Rust books and websites were also written for people coming from Java, C#, and other such languages.
Now, a lot more people are learning Rust as a first language. For those people, a book that starts with examples in other languages is going to be confusing. Learn Rust in a Month of Lunches doesn’t assume that you know general programming terminology: words like generics, pointers, stack and heap memory, arguments, expressions, concurrency, and so on. All of these terms are explained one by one.
Almost all of Learn Rust in a Month of Lunches is written using the online Rust Playground, which requires nothing to install. You can, of course, use VS Code or some other IDE you have installed, but you don’t need to. The book intends to be easy in this sense, too: you should be able to learn most of the language just by opening up a tab in your browser.
Who should read this book
Learn Rust in a Month of Lunches has a single goal: to be the absolute easiest way for anyone to learn Rust as quickly as possible. I like to think of the book’s target audience as these types of people:
People who are ambitious and want to learn Rust as quickly as possible—The simple English used in the book gets out of your way and lets you focus more on Rust itself.
People with English as a second language—Most developers are good enough at English that reading documentation is easy enough, but a full book of wordy and complex English can be a bit of a burden for some.
People who are curious but don’t have enough time in the day and just want to get to the information—Maybe you only have 30 minutes a day to devote to Rust. Without any flowery language, you can use those 30 minutes as effectively as possible to get to the information you want.
People who have read another introductory Rust book and want to go over the basics again with something new.
People who have tried to learn Rust, but it still hasn’t clicked—Hopefully this book will be the one that does the trick!
How this book is organized: A road map
Learn Rust in a Month of Lunches is organized into 24 separate chapters but not into thematic sections as one often sees in a book of its type. That said, the book could be divided into parts that represent the amount of mental effort required.
Chapters 1 to 6 are a steady progression from Rust’s simplest types and concepts to making your own types, working with advanced collection types, and, finally, error handling and some of the first types and concepts that make Rust quite unique. By the end of this section, you will have a feel for what makes Rust the language it is and eager to dive into the rest.
Chapters 7 to 12 are packed to the brim with new concepts and are the chapters where Rust will finally start to click. This is probably the most fascinating yet mentally taxing part of the book. It deals with understanding traits, iterators, closures, lifetimes, interior mutability, multiple threads, and even a type called Cow.
Chapters 13 to 16 are where the pace of learning starts to ease up a bit. Many new concepts are introduced here as well, but they go in hand with beginning to look at how to start building software in Rust, how to test it, and other tips and tricks involving patterns you will use often as a Rust developer.
In Chapters 17 to 19, the book begins to get into external crates: code written by others for you to use in your own programs. This is the point at which we’ll begin to assume that you have Rust installed on your computer. It is also the point at which we will learn about async Rust, which is encountered quite a bit in external crates.
Chapters 20 and 21 are a fun tour of the standard library. In these two chapters, we kick back and relax for a bit and see what parts of the standard library we haven’t come across yet.
Chapter 22 is about macros, a way to generate code before the compiler begins looking at it. If you walk away from this chapter with a general understanding of how to read macros and when you might use them, it will have done its job.
Chapter 23 and 24 are the last chapters of the book and are a fun send-off. Each of these chapters contains three unfinished projects for you to pick up and develop on your own. Each of the six projects compiles and accomplishes its basic objectives but is left incomplete on purpose to encourage you to make your own changes and add to them.
About the code
This book contains many examples of source code both in numbered listings and in line with normal text. In both cases, source code is formatted in a fixed-width font like this to separate it from ordinary text.
In many cases, the original source code has been reformatted; we’ve added line breaks and reworked indentation to accommodate the available page space in the book. In some cases, even this was not enough, and listings include line-continuation markers (➥). Additionally, comments in the source code have often been removed from the listings when the code is described in the text. Code annotations accompany many of the listings, highlighting important concepts.
You can get executable snippets of code from the liveBook (online) version of this book at https://livebook.manning.com/book/learn-rust-in-a-month-of-lunches. The complete code for the examples in the book is available for download from the Manning website at https://www.manning.com/books/learn-rust-in-a-month-of-lunches.
liveBook discussion forum
Purchase of Learn Rust in a Month of Lunches includes free access to liveBook, Manning’s online reading platform. Using liveBook’s exclusive discussion features, you can attach comments to the book globally or to specific sections or paragraphs. It’s a snap to make notes for yourself, ask and answer technical questions, and receive help from the author and other users. To access the forum, go to https://livebook.manning.com/book/learn-rust-in-a-month-of-lunches/discussion. You can also learn more about Manning’s forums and the rules of conduct at https://livebook.manning.com/discussion.
Manning’s commitment to our readers is to provide a venue where a meaningful dialogue between individual readers and between readers and the author can take place. It is not a commitment to any specific amount of participation on the part of the author, whose contribution to the forum remains voluntary (and unpaid). We suggest you try asking the author some challenging questions lest his interest stray! The forum and the archives of previous discussions will be accessible from the publisher’s website as long as the book is in print.
about the author
Dave MacLeod
is a Canadian who has lived in Korea since 2002, living in Japan a few years before that. As a child in the 1980s, he made small role-playing games in BASIC on the family’s ADAM computer, but after a local Logo competition, he decided that programming was not for him. He felt the urge to code again in the early 2010s, but it was not until he came across Rust that he found a language to devote himself to. He speaks a number of natural languages, including Korean and Japanese, as well as the constructed language Occidental. He has worked as an educator, translator, project controller, and copywriter before becoming a full-time Rust developer in Seoul, Korea.
1 Some basics
This chapter covers
Introducing Rust
Using comments (putting human-readable hints in your code)
Some primitive types (simple numbers and other simple types)
Type inference (how Rust knows the type)
Hello, World!
and printing
Declaring variables and code blocks
Shadowing (giving variables the same name)
This first chapter is as easy as Rust gets and has a bit of everything to get started. You’ll notice that even in Rust’s easiest data types, there’s a strong focus on the bits and bytes that make up a computer’s system. That means there’s quite a bit of choice, even in simple types like integers. You’ll also start to get a feel for how strict Rust is. If the compiler isn’t satisfied, your program won’t run! That’s a good thing—it does a lot of the thinking for you.
1.1 Introducing Rust
The Rust language was only released in 2015 and, as of 2024, isn’t even a decade old. It’s quite new but already very popular, appearing just about everywhere you can think of—Windows, Android, the Linux kernel, Amazon Web Services (AWS), Discord, Cloudflare, you name it. It is incredible how popular Rust has become after less than a decade. Rust earned its popularity by giving you almost everything you could want in a language: the speed and control of languages like C or C++, the memory safety of other newer languages like Python, a rich type system that lets you avoid bugs, and a friendly compiler that helps you when you go wrong. It does this with some new ideas that are sometimes different from other languages. That means there are some new things to learn, and you can’t just figure it out as you go along.
Rust is a language you have to think about for a while to understand.
So Rust is a language that is famously difficult to learn. But I don’t agree that Rust is difficult. Programming itself is difficult. Rust simply shows you these difficulties when you are writing your code, not after you start running it. That’s where this saying comes from: In Rust, you get the hangover first.
In many other languages, the party starts first: your code compiles and looks great! Then you run your code, and there’s a lot to debug. That’s when you get the hangover.
The hangover in Rust is because you have to satisfy the compiler that you are writing correct code. If your code doesn’t satisfy the compiler, it won’t run. You can’t mix types together, you have to handle possible errors, you have to decide what to do when a value might be missing, etc. But as you do that, the compiler gives you hints and suggestions to fix your code so that it will run. It’s tough work, but the compiler tries to guide you along the way. And when your code finally compiles, it works great.
In fact, because of that, Rust was the first language that I was properly able to learn. I loved how friendly the compiler was when my code didn’t compile. The compiler felt like a teacher or a co-programmer. It was also interesting how the errors taught me about how computers use memory. Rust wasn’t just a language that let me build software; it was a language that taught me details about the inner workings of computers that I never knew before. The more I used it, the more I wanted to know, and that’s why I was able to learn Rust as my first language. I hope this book will help others learn it, too, even if Rust is their first programming language.
1.1.1 A pep talk
Rust is a fairly easy language. Seriously! Well, sort of. Yes, it’s complex and takes a lot of work to learn. Yes, most people who learn Rust have frustrating days (sometimes unbearingly frustrating days) where they just want their code to compile and don’t understand what to do.
But this period does not last forever. After the period is over, Rust becomes easier because it starts doing a lot of the thinking for you. Rust is the type of language that allows junior developers to start working on an existing code base with confidence because, for the most part, it simply won’t compile if there’s a problem with your code. Sometimes you hear horror stories about junior developers who join a company and simply aren’t able to contribute yet. They see a code base and ask if they can make a change, but the senior developers say not to touch it because it’s working and who knows what will happen if you make a change.
Rust isn’t like that.
That makes contributing and refactoring code, well, easy. If you watch Rust live streams on YouTube or Twitch, you’ll see this happen a lot. The streamer will make a bunch of changes to some existing code and then say, Okay, let’s see what breaks.
The compiler then gives a few dozen messages showing what parts don’t work anymore, and then the streamer hunts them down one by one and makes the necessary changes until it compiles again—usually in just a few minutes. Not a lot of languages can do that.
1.1.2 Rust is like a critical spouse
A great analogy for Rust is that of a critical but helpful spouse. Imagine you have a job interview and are getting ready to head out the door and ask your spouse how you look. Let’s see how two types of spouses treat you: the lenient language spouse and the strict Rust spouse.
The lenient language spouse sees you going out the door and calls out: You look great, honey! Hope the interview goes well!
And off you go! You’re feeling good. But maybe you don’t look great and don’t realize it. Maybe you forgot to prepare a number of important things for the interview. If you’re an expert in interviews, you’ll do fine, but if not, you might be in trouble.
The Rust spouse isn’t so lenient and won’t even let you out the door: You’re going out wearing that? It’s too hot today; you’ll be sweating by the time you get in. Put on that suit with the lighter fabric.
You change your suit.
The Rust spouse looks at you again and says, The suit you just changed into doesn’t match your socks. You need to change to grey socks.
You grumble and go change your socks.
The Rust spouse still isn’t satisfied: It’s windy today, and it’s at least a quarter-mile walk from the parking lot to the company. Your hair is going to be messy by the time you get there. Put some gel in.
You go back to the bathroom and put some gel in your hair.
The Rust spouse says, You still can’t go. The parking lot you’ll be using was built a long time ago and doesn’t take credit cards. You need $2.50 in change for the machine. Find some change.
Sigh. You go and look around for some loose change. Finally, you gather $2.50.
This repeats and repeats another 10 times. You’re starting to get annoyed, but you know your spouse is right. You make yet another change. Is it the last one?
Eventually, your Rust spouse looks you up and down, thinks a bit, and says: Fine. Off you go.
Yes! Finally! That was a lot of work.
You head out the door, still a bit frustrated by all the changes you had to make. But you walk by a window and see your reflection. You look great! It’s windy today, but your hair isn’t being blown around. You pull into the parking lot and put in the $2.50—just the right amount of change.
You look around and see someone else arriving for the interview in a suit that’s too heavy and is already sweating. His socks don’t match the suit. He only has a credit card and is trying to find a store nearby to get some change. He starts walking to the store, his hair in a mess as the wind blows it every which way. But not you—your spouse did half of the work for you before you even started. So, in that sense, Rust is a really easy language.
If you think about it, programs live at run time, but programmers can only see up to compile time—the time before a program starts. If your code compiles, you run it and hope for the best. You can’t control the program anymore once it starts.
If your language isn’t strict at compile time, most of the possible errors will happen at run time instead, and you will have to debug them. Rust is as strict as possible at compile time, where you, the programmer, live. So Rust teaches you as much as it can about your program before you even run it.
Okay, what does this actually look like in practice? Let’s take a look at a real example. We’ll go to the Rust Playground (https://play.rust-lang.org/), write some incorrect Rust code, and see what happens. We’ll try to make a String and then push a single character to it and print it out:
fn main() { let my_name: String = Dave
; my_name.push(!
); println!({}
my_name); }
This is pretty good for a first try at Rust, but it’s not correct yet. What does the Rust compiler have to say about that? Quite a bit, in fact. It gives you three suggestions:
error: expected `,`, found `my_name` | 4 | println!({}
my_name); | ^^^^^^^ expected `,` error[E0308]: mismatched types --> src/main.rs:2:27 | 2 | let my_name: String = Dave
; | ------ ^^^^^^- help: try using a conversion method: ➥`.to_string()` | | | | | expected struct `String`, found `&str` | expected due to this error[E0308]: mismatched types --> src/main.rs:3:18 | 3 | my_name.push(!
); | ---- ^^^ expected `char`, found `&str` | | | arguments to this function are incorrect | help: if you meant to write a `char` literal, use single quotes | 3 | my_name.push('!'); | ~~~
If you do what the compiler suggests, it will look like this:
fn main() { let my_name: String = Dave
.to_string(); my_name.push('!'); println!({}
, my_name); }
If you click Run again, you’ll see the compiler now has a little more to say:
error[E0596]: cannot borrow `my_name` as mutable, as it is not declared as ➥mutable --> src/main.rs:3:5 | 2 | let my_name: String = Dave
.to_string(); | ------- help: consider changing this to be mutable: `mut ➥my_name` 3 | my_name.push('!'); | ^^^^^^^^^^^^^^^^^ cannot borrow as mutable
If you follow its advice here, you’ll end up with this code:
fn main() { let mut my_name: String = Dave
.to_string(); my_name.push('!'); println!({}
, my_name); }
And it works! That’s the combination of strictness and helpfulness that the Rust compiler is famous for. You will understand all of this code within just a few chapters, so don’t worry about it too much now.
One final note before we get into chapter 1: the Rust compiler is smart enough to know if you wrote some code you never used. In that case, it will give you a warning so that you will remember that you wrote something you haven’t used yet. In this book, many examples have code to teach a concept and never gets used, so don’t worry about those warnings.
This code, for example, compiles and runs just fine:
fn main() { let my_number = 9; }
But when you run it, Rust will generate a warning:
warning: unused variable: `my_number` --> src/main.rs:2:9 | 2 | let my_number = 9; | ^^^^^^^^^ help: if this is intentional, prefix it with an ➥underscore: `_my_number` | = note: `#[warn(unused_variables)]` on by default
This is a hint from the compiler to let you know that you created a variable but didn’t do anything with it. It doesn’t mean there is a problem with your code, so don’t worry.
Let’s get started!
1.2 Comments
Comments are made for programmers to read, not the computer. It’s good to write comments to help other people understand your code. It’s also good to help you understand your code later (many people write good code but then forget why they wrote it). To write comments in Rust, you usually use // like in the following example:
fn main() { // Rust programs start with fn main() // You put the code inside a block. It starts with { and ends with } let some_number = 100; // We can write as much as we want here and the ➥compiler won't look at it }
When you write a // comment, the compiler won’t look at anything to the right of the //.
The let some_number = 100; part of the code, by the way, is how you make variables in Rust. A variable is basically a piece of data with a name chosen by us—hopefully a good name—so that later on we will remember what sort of data the variable is holding. Here, we are telling Rust to take this piece of data (the number 100) and give it the name some_number so that we can use some_number later to access the number 100 it holds. The variable name could differ depending on the context: we might write let perfect_score = 100;, for example, if the number 100 represented a perfect score on a test.
There is another kind of comment that you write with /* to start and */ to end. A comment wrapped in /* and */ is useful to write in the middle of your code:
fn main() let some_number/*: i16*/ = 100; }
To the compiler, let some_number/*: i16*/ = 100; looks like let some_number = 100;. The /* */ form is also useful for very long comments of more than one line. In the following example, you can see that you need to write // for every line. But if you type /*, the comment won’t stop until you finish it with */:
fn main() { let some_number = 100; // Let me tell you // a little about this number. // It's 100, which is my favorite number. // It's called some_number but actually I think that... let some_number = 100; /* Let me tell you a little about this number. It's 100, which is my favorite number. It's called some_number but actually I think that... */ }
If you see /// (three slashes), that’s a doc comment
(documentation comment). A doc comment can be automatically made into documentation for your code. Documentation is used to explain how code works, usually for other people to read, but it can be good for you, too, so you won’t forget. All the information on documentation pages like http://doc.rust-lang.org/std/index.html is made with doc comments.
So // means comments for inside the code, while /// is for more official information to be shared beyond the code itself. Regular // comments can be very informal, like this:
// todo: delete this after Fred updates the client.
But /// comments are for outsiders reading your code and tend to be more formal, like:
/// Converts a string slice in a given base to an integer. Leading and trailing whitespace represent an error.
(We’ll look at doc comments later in the book. But if you have Rust installed already and are curious, try writing some comments and then typing cargo doc --open to see what happens.)
So comments are pretty easy because Rust doesn’t notice them at all. Let’s move on to another pretty easy subject: Rust’s simplest types.
1.3 Primitive types: Integers, characters, and strings
Rust has many types that let you work with numbers, characters, and so on. Some are simple, and others are more complicated; you can even create your own.
The simplest types in Rust are called primitive types (primitive = very basic). We will start with two of them: integers and characters. Rust has a lot of integer types, but they all have one thing in common: they are whole numbers with no decimal point. There are two types of integers: signed integers and unsigned integers.
So what does signed mean exactly? It’s simple: signed means + (plus sign) and − (minus sign). So, signed integers can be positive or negative (e.g., +8, −8) or zero. But unsigned integers (e.g., 8) can only be nonnegative because they do not have a sign. The signed integer types are i8, i16, i32, i64, i128, and isize. The unsigned integer types are u8, u16, u32, u64, u128, and usize.
The number after the i or the u means the number of bits for the number, so numbers with more bits can be larger: 8 bits = 1 byte, so i8 is 1 byte, i64 is 8 bytes, and so on. Number types with more bits can hold much larger numbers:
u8 can hold a number as large 255.
u16 can hold a number as large as 65,535.
u128 can hold a number as large as 340,282,366,920,938,463,463,374,607,431,768,211,455.
A quick explanation of how integers work: computers use binary numbers, while people use decimals. Binary means 2, and decimal means 10, so you have two possible digits for binary (0 or 1) and 10 possible digits (0 to 9) for decimal.
With decimals, you move up by 10 at a time: 100 is 10 times more than 10, 1,000 is 10 times more than 100, and so on. But computers increase numbers in binary by 2, not 10. Here’s what this doubling looks like over the 8 bits of a u8.
You can see that there are eight spaces for numbers, which are the bits. Each bit is for a number two times larger than the last one. A bit can be a 0 or a 1—nothing else. When a bit shows up as 0, the number isn’t counted; if it shows up as 1, it is counted.
If you have a decimal number with eight digits, the highest number you can get is 99,999,999. Reading from right to left, you can think of this number as being made of a 9, a 90, a 900, a 9,000, a 90,000, a 900,000, a 9,000,000, and a 90,000,000. Put them all together, and you get 99,999,999. Now, if you do the same for binary, the highest number you can get over eight digits is 11111111. And if you count up these numbers, you get 1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 = 255. That’s why 255 is the largest size for a u8. And if you move to a u16, you have eight more spaces, each one two times larger than the last. So a u16 is all those plus 256, then 512, and so on. Consequently, the highest number for a u16 is 65,535 (a lot higher), even though it’s only two times the size (16 bits, or 2 bytes).
You can also think of it as this: a human cashier at the grocery who asks you to pay $226 is asking for
six 1s (6)
two 10s (20)
two 100s (200)
But what a machine cashier
asks you for is 11100010, which is (remember, going from right to left):
no 1s
one 2
no 4s
no 8s
no 16s
one 32
one 64
one 128
Putting all that together, you get: 2 + 32 + 64 + 128 = 226. And that’s why the u8 for 226 looks like this.
Signed integers have a maximum value that is only half that of an unsigned type of the same number of bits because they also have to represent negative numbers. So a u8 goes from 0 to 255 while an i8 goes from −128 to 127.
So what about isize and usize, and why are there no numbers in their name? These two types have a number of bits depending on your type of computer. (The number of bits on your computer is called the architecture of your computer.) So isize and usize on a 32-bit computer is like i32 and u32, and isize and usize on a 64-bit computer is like i64 and u64.
There are many reasons why Rust has a lot of integer sizes. One reason is com-puter performance: a smaller number of bytes can be faster to process. For example, the number –10 as an i8 is 11110110, but as an i64, it is 1111111111111111111111111111111111111111111111111111111111110110. The lar-ger type has a greater maximum number but still uses the same number of bits, even if the number is a small one. But there are quite a few other reasons for having a lot of integer sizes. One is related to the char type, which is related to one of Rust’s integer types.
Characters in Rust are called char. Every char has a number: the letter A is number 65, while the character 友 is number 21451. The list of numbers is called Unicode. Unicode uses smaller numbers for basic characters like A through Z, digits 0 through 9, or space. New languages get added to Unicode all the time, and some languages have thousands of characters, which is why 友 is such a high number.
As you can see, a char can be a lot of things, even an emoji:
fn main() { let first_letter = 'A'; let space = ' '; ① let other_language_char = 'Ꮔ'; ② let cat_face = '😺'; ③ }
① A space inside ' ' is also a char.
② Thanks to Unicode, other languages like Cherokee display just fine, too.
③ Emojis are chars, too!
So you won’t be able to fit all chars into something as small as a u8, for example. But the characters used most (called ASCII) are represented by numbers less than 256, and they can fit into a u8. Remember, a u8 is 0 plus all the numbers up to 255, for 256 characters in total. This means that Rust can safely cast
a u8 into a char, using as. (Cast a u8 as a char
means turn a u8 into a char.
)
Casting with as is useful because Rust is very strict. It always needs to know the type and won’t let you use two different types together, even if they are both integers. For example, this will not work:
fn main() { ① let my_number = 100; ② println!({}
, my_number as char); }
① main() is where Rust programs start to run. Code goes inside {} (known as braces or curly brackets).
② We didn’t say which integer type it will be, so Rust chooses i32. Rust always chooses i32 for integers if you don’t tell it to use a different type.
Here is the reason:
error[E0604]: only u8 can be cast as char, not i32 --> src\main.rs:3:20 | 3 | println!({}
, my_number as char); | ^^^^^^^^^^^^^^^^^
By the way, you’ll see println!, {}, and {:?} in this chapter a bit. Typing println! will print and then add a new line, while {} and {:?} describe what type of printing. println! is known as a macro. A macro is a function that writes code for you; all macros have a ! after them. You don’t need to worry about remembering to add the ! because the compiler will notice if you don’t:
fn main() { let my_number = 100; println({}
, my_number); }
The compiler tells us exactly what to do:
error[E0423]: expected function, found macro `println` --> src/main.rs:3:5 | 3 | println({}
, my_number); | ^^^^^^^ not a function | help: use `!` to invoke the macro | 3 | println!({}
, my_number); |
We will learn more about printing in this and the next chapter.
Now, back to our my_number as char problem. Fortunately, we can easily fix this with as. We can’t cast i32 as a char, but we can cast an i32 as a u8. Then we can do the same from u8 to char. So, in one line, we use as to make my_number a u8 and again to make it a char. Now it will compile:
fn main() { let my_number = 100; println!({}
, my_number as u8 as char); }
It prints d because that is the char in place 100.
So casting can be convenient. But be careful: when you cast a large number into a smaller type, some unexpected things can happen. For example, a u8 can go up to 255. What happens if you cast the number 256 into a u8?
fn main() { let my_number = 256; println!({}
, my_number as u8); }
You might think it would cut it down to 255, the largest possible size, but it returns a 0.
What happens if you cast an i32 600 to a u8?
fn main() { let my_number = 600; println!({}
, my_number as u8); }
Now it returns an 88. You can probably see what it’s doing now: every time it passes the largest possible number, it starts at 0 again. So when you cast a 600 to a u8, it passes the largest possible u8 two times, and then there are 88 left. You can think of it mathematically as 600 − 256 − 256 = 88. So be a little careful when casting into a smaller type! When casting, make sure the old number isn’t larger than the new type’s largest possible number.
In fact, casting is somewhat rare in Rust because there is usually no need for it. For example, you don’t need to use a cast to get a u8. You can just tell Rust that my_ number is a u8. Here’s how you do it:
fn main() { let my_number: u8 = 100; ① println!({}
, my_number as char); }
① Change my_number to my_number: u8.
So those are two reasons for all the different number types in Rust. Here is another reason: usize is the size Rust uses for indexing. (Indexing means which item is first,
which item is second,
etc.) A usize is the best size for indexing because
An index can’t be negative, so it needs to be an unsigned integer with a u.
It should have a lot of space because index numbers can get quite large (but it can’t be a u64 because 32-bit computers can’t use a u64).
So Rust uses usize so that your computer can get the biggest number for indexing that it can read.
Let’s learn some more about char. You saw that a char is always one character and uses ' ' (single quotes) instead of
(double quotes).
All chars use 4 bytes of memory, since 4 bytes are enough to hold any kind of character:
Basic letters and symbols usually need 1 byte, (e.g., a b 1 2 + - = $ @).
Other letters like German umlauts or accents need 2 bytes (e.g., ä ö ü ß è à ñ).
Korean, Japanese, or Chinese characters need 3 or 4 bytes (e.g., 国안녕).
So, to be sure that a char can be any of these, it needs to be 4 bytes. With 2 bytes (a u16), the largest number you can make is 65,535, which is well below the number of letters in all the languages in the world (Chinese characters alone are more than this!). But a u32 (4 bytes) offers more than enough space, allowing for up to 4,294,967,295 letters, which is why a char is a u32 on the inside.
But always using 4 bytes is just for the char type. Strings are different and don’t always use 4 bytes per single character. When a character is part of a string (not the char type), the string is encoded to use the least amount of memory needed for each character.
We can use a method called .len() to see this for ourselves. Try copying and pasting this and clicking Run:
fn main() { println!(Size of a char: {}
, std::mem::size_of::Size of a: {}
, a
.len()); println!(Size of ß: {}
, ß
.len()); println!(Size of 国: {}
, 国
.len()); println!(Size of 𓅱: {}
, 𓅱
.len()); }
(By the way, std::mem means the part of the standard library called mem where this size_of() function is. The :: symbol is used sort of like a path to an address. It’s sort of like writing USA::California::LosAngeles. We will learn about this later.)
The previous code prints the following:
Size of a char: 4 Size of a: 1 Size of ß: 2 Size of 国: 3 Size of 𓅱: 4
You can see that a is 1 byte, the German ß is 2, the Japanese 国 (meaning country) is 3, and the ancient Egyptian 𓅱 (a quail chick) is 4 bytes.
Let’s try printing the length of two strings, one with six letters and the other with three letters. Interestingly, the second one is larger:
fn main() { let str1 = Hello!
; println!(str1 is {} bytes.
, str1.len()); let str2 = 안녕!
; ① println!(str2 is {} bytes.
, str2.len()); }
① Korean for Hi
This prints
str1 is 6 bytes. str2 is 7 bytes.
str1 is six characters in length and 6 bytes, but str2 is three characters in length and 7 bytes. So be careful! The .len() method returns the number of bytes, not the number of letters or characters.
By the way, the size of a byte is one u8: it’s a number that goes from 0 to 255. We can use a method called .as_bytes() to see what these strings look like as bytes:
fn main() { println!({:?}
, a
.as_bytes()); println!({:?}
, ß
.as_bytes()); println!({:?}
, 国
.as_bytes()); println!({:?}
, 𓅱
.as_bytes()); }
You can see that each one is different and that to show them all in a single type, it needs 4 bytes. And that’s why the char type is 4 bytes long:
[97] [195, 159] [229, 155, 189] [240, 147, 133, 177]
Now, if .len() gives the size in bytes, what about the size in characters? You can find this out by using two methods together. We will learn about these methods in more detail later in the book (especially chapter 8), but for now, you can just remember that .chars().count() will give you the number of characters or letters, not bytes. Calling .chars() first turns a string into a collection of characters, and then .count() counts how many of them there are.
Let’s give that a try:
fn main() { let str1 = Hello!
; println!(str1 is {} bytes and also {} characters.
, str1.len(), str1.chars().count()); let str2 = 안녕!
; println!(str2 is {} bytes but only {} characters.
, str2.len(), str2.chars().count()); }
This prints
str1 is 6 bytes and also 6 characters. str2 is 7 bytes but only 3 characters.
You might have noticed already that you don’t usually need to tell Rust the type of variable you’re making. The Rust compiler is happy with let letter = 'ß' and doesn’t make you type let letter: char = 'ß' to declare a char. Let’s learn why!
1.4 Type inference
The term type inference means that Rust can usually decide what type a variable is even if you don’t tell it. The term comes from the verb infer, which means to make an educated guess.
The compiler is smart enough that it can usually infer
the types that you are using. In other words, it always needs to know the type of variables you are using, but most of the time, you don’t need to tell it. For example, if you type let my_number = 8, the variable my_number will be an i32. That is because the compiler chooses i32 for integers unless you tell it to choose a different integer type. But if you say let my_number: u8 = 8, it will make my_number a u8 because you told it to make a u8 instead of an i32.
So, usually, the compiler can guess. But sometimes you need to tell it, usually for two reasons:
You are doing something very complex, and the compiler can’t determine the type you want.
You simply want a different type (e.g., you want an i128, not an i32).
To specify a type, add a colon after the variable name:
fn main() { let small_number: u8 = 10; }
For numbers, you can add the type after the number. You don’t need a space—just type it right after the number:
fn main() { let small_number = 10u8; ① }
① Same as u8 = 10
You can also add _ if you want to make the number easy to read:
fn main() { let small_number = 10_u8; let big_number = 100_000_000_i32; }
The _ is only to make numbers easy for humans to read and does not affect the number. It is completely ignored by the compiler. In fact, it doesn’t matter how many _ you use:
fn main() { let number = 0________u8; let number2 = 1___6______2____4______i32; println!({}, {}
, number, number2); }
This prints 0, 1624.
Interestingly, if you add a decimal point to a number, it won’t be an integer (a whole number) anymore. Rust will instead make a float, which is an entirely different type of number. Let’s learn how floats work now.
1.5 Floats
Floats are numbers with decimal points. 5.5 is a float, and 6 is an integer. 5.0 is also a float, and