Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Discover millions of ebooks, audiobooks, and so much more with a free trial

Only $11.99/month after trial. Cancel anytime.

Learn Rust in a Month of Lunches
Learn Rust in a Month of Lunches
Learn Rust in a Month of Lunches
Ebook1,047 pages6 hours

Learn Rust in a Month of Lunches

Rating: 0 out of 5 stars

()

Read preview

About this ebook

One month. One hour a day. That’s all it takes to start writing Rust code!

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
LanguageEnglish
PublisherManning
Release dateApr 30, 2024
ISBN9781638354468
Learn Rust in a Month of Lunches
Author

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

Programming For You

View More

Related articles

Reviews for Learn Rust in a Month of Lunches

Rating: 0 out of 5 stars
0 ratings

0 ratings0 reviews

What did you think?

Tap to rate

Review must be at least 10 words

    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::());     println!(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

    Enjoying the preview?
    Page 1 of 1