https://github.com/ragingwind/rust-programming-language-summary
Summary, Short version of The Rust Programming Language
https://github.com/ragingwind/rust-programming-language-summary
Last synced: 29 days ago
JSON representation
Summary, Short version of The Rust Programming Language
- Host: GitHub
- URL: https://github.com/ragingwind/rust-programming-language-summary
- Owner: ragingwind
- Created: 2019-06-25T21:47:46.000Z (about 7 years ago)
- Default Branch: main
- Last Pushed: 2023-04-16T06:55:24.000Z (about 3 years ago)
- Last Synced: 2025-04-01T17:13:09.929Z (over 1 year ago)
- Language: Rust
- Size: 854 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: readme.md
Awesome Lists containing this project
README
- [The Rust Programming Language Summary](#the-rust-programming-language-summary)
- [Version](#version)
- [Foreword](#foreword)
- [Introduction](#introduction)
- [Who Rust Is For](#who-rust-is-for)
- [Teams of Developers](#teams-of-developers)
- [Students](#students)
- [Companies](#companies)
- [Open Source Developers](#open-source-developers)
- [People Who Value Speed and Stability](#people-who-value-speed-and-stability)
- [Getting Started](#getting-started)
- [Installation](#installation)
- [Hello, World!](#hello-world)
- [Hello, Cargo](#hello-cargo)
- [Programming a Guessing Game](#programming-a-guessing-game)
- [Setting Up a New Project](#setting-up-a-new-project)
- [Processing a Guess](#processing-a-guess)
- [Common Programming Concepts](#common-programming-concepts)
- [Variables and Mutability](#variables-and-mutability)
- [Differences Between Variable and Constants](#differences-between-variable-and-constants)
- [Shadowing](#shadowing)
- [Data Types](#data-types)
- [Scalar Types](#scalar-types)
- [Compound Types](#compound-types)
- [Functions](#functions)
- [Parameters](#parameters)
- [Statements and Expressions](#statements-and-expressions)
- [Functions with Return Values](#functions-with-return-values)
- [Comments](#comments)
- [Control Flow](#control-flow)
- [if expression](#if-expression)
- [Repeatition with Loops](#repeatition-with-loops)
- [Understanding Ownership](#understanding-ownership)
- [What is Ownership?](#what-is-ownership)
- [Ownership Rules](#ownership-rules)
- [Variable Scope](#variable-scope)
- [The String Type](#the-string-type)
- [Memory and Allocation](#memory-and-allocation)
- [Ways Variables and Data Interact: Move](#ways-variables-and-data-interact-move)
- [Ways Variables and Data Interact: Clone](#ways-variables-and-data-interact-clone)
- [Stack-Only Data: Copy](#stack-only-data-copy)
- [Ownership and Function](#ownership-and-function)
- [Return Values and Scope](#return-values-and-scope)
- [Returning with Tuple](#returning-with-tuple)
- [References and Borrowing](#references-and-borrowing)
- [Mutable References](#mutable-references)
- [Mutable refernces restriction](#mutable-refernces-restriction)
- [Multiple mutable refernes by creating a new scope](#multiple-mutable-refernes-by-creating-a-new-scope)
- [Immutable references](#immutable-references)
- [Dangling References](#dangling-references)
- [The Slice Type](#the-slice-type)
- [String Slices](#string-slices)
- [String literals are slices](#string-literals-are-slices)
- [String Slices as Paramters](#string-slices-as-paramters)
- [Other slices](#other-slices)
- [Using Structs to Structre Related Data](#using-structs-to-structre-related-data)
- [Defining and Instantiation Structs](#defining-and-instantiation-structs)
- [Using the Field Init Shorthand](#using-the-field-init-shorthand)
- [Creating instances from other instnace with update value, or struct update syntax](#creating-instances-from-other-instnace-with-update-value-or-struct-update-syntax)
- [Using Tuple Structs Without Named Fields to Create Different Types](#using-tuple-structs-without-named-fields-to-create-different-types)
- [Unit-like Structs without Any Field](#unit-like-structs-without-any-field)
- [An Example Program Using Struct](#an-example-program-using-struct)
- [Refactoring with Tuples](#refactoring-with-tuples)
- [Refactoring with Structs: Adding More Meaning](#refactoring-with-structs-adding-more-meaning)
- [Adding Useful Functionality with Derived Traits](#adding-useful-functionality-with-derived-traits)
- [Method Syntax](#method-syntax)
- [Defining Methods](#defining-methods)
- [Where’s the -\> Operator?](#wheres-the---operator)
- [Methods with More Parameters](#methods-with-more-parameters)
- [Associated Functions](#associated-functions)
- [Multiple impl Blocks](#multiple-impl-blocks)
- [Enums and Pattern Matching](#enums-and-pattern-matching)
- [Defining an Enum](#defining-an-enum)
- [The `Option` Enum and Its Advantages Over Null Values](#the-option-enum-and-its-advantages-over-null-values)
- [Use Some Value with various method](#use-some-value-with-various-method)
- [Some Value usecases](#some-value-usecases)
- [The `match` Control Flow Operator](#the-match-control-flow-operator)
- [Patterns That Bind to Values](#patterns-that-bind-to-values)
- [Matching with `Option`](#matching-with-optiont)
- [Matches Are Exhaustive](#matches-are-exhaustive)
- [Catch-all Patterns and the \_ Placeholder](#catch-all-patterns-and-the-_-placeholder)
- [Concise Control FLow with `if let`](#concise-control-flow-with-if-let)
- [Match one pattern](#match-one-pattern)
- [With else](#with-else)
- [Managing Growing Projects with Packages, Crates, and Modules](#managing-growing-projects-with-packages-crates-and-modules)
- [Packages and Crates](#packages-and-crates)
- [Defining Modules to Control Scope and Privacy](#defining-modules-to-control-scope-and-privacy)
- [Modules Cheat Sheet](#modules-cheat-sheet)
- [Grouping Related Code in Modules](#grouping-related-code-in-modules)
- [Paths for Referring to an Item in the Module Tree](#paths-for-referring-to-an-item-in-the-module-tree)
- [Starting Relative Paths with `super`](#starting-relative-paths-with-super)
- [Making Structs and Enums Public](#making-structs-and-enums-public)
- [Bringing Paths into Scope with the use Keyword](#bringing-paths-into-scope-with-the-use-keyword)
- [Creating Idiomatic use Paths](#creating-idiomatic-use-paths)
- [Providing New Names with the as Keyword](#providing-new-names-with-the-as-keyword)
- [Re-exporting Names with pub use](#re-exporting-names-with-pub-use)
- [Using External Packages](#using-external-packages)
- [Using Nested Paths to Clean Up Large use Lists](#using-nested-paths-to-clean-up-large-use-lists)
- [The Glob Operator](#the-glob-operator)
- [Separating Modules into Different Files](#separating-modules-into-different-files)
- [Common Collections](#common-collections)
- [Storing Lists of Values with Vectors](#storing-lists-of-values-with-vectors)
- [Creating a New Vector](#creating-a-new-vector)
- [Update a Vector](#update-a-vector)
- [Reading Elements of Vectors](#reading-elements-of-vectors)
- [Iterating over the values in a Vector](#iterating-over-the-values-in-a-vector)
- [Using an enum to store multiple types](#using-an-enum-to-store-multiple-types)
- [Dropping a Vector Drops Its Elements](#dropping-a-vector-drops-its-elements)
- [Storing UTF-8 Encoded Text With Strings](#storing-utf-8-encoded-text-with-strings)
- [What Is a String?](#what-is-a-string)
- [Creating a New String](#creating-a-new-string)
- [Updading a String](#updading-a-string)
- [Appending to a String with push\_str and push](#appending-to-a-string-with-push_str-and-push)
- [Concatenation with the + Operator or the format! Macro](#concatenation-with-the--operator-or-the-format-macro)
- [Indexing into Strings](#indexing-into-strings)
- [Internal Representation](#internal-representation)
- [Bytes and Scalar Values and Grapheme Clusters! Oh My!](#bytes-and-scalar-values-and-grapheme-clusters-oh-my)
- [Slicing Strings](#slicing-strings)
- [Methods for Iterating Over Strings](#methods-for-iterating-over-strings)
- [Strings Are Not So Simple](#strings-are-not-so-simple)
- [Storing Keys with Assoicated Values in Hash Maps](#storing-keys-with-assoicated-values-in-hash-maps)
- [Creating a New Hash Map](#creating-a-new-hash-map)
- [Accessing Values in a Hash Map](#accessing-values-in-a-hash-map)
- [Hash Maps and Ownership](#hash-maps-and-ownership)
- [Updating a Hash Map](#updating-a-hash-map)
- [Overwriting a Value](#overwriting-a-value)
- [Adding a Key and Value Only If a Key Isn’t Present](#adding-a-key-and-value-only-if-a-key-isnt-present)
- [Updating a Value Based on the Old Value](#updating-a-value-based-on-the-old-value)
- [Hashing Functions](#hashing-functions)
- [Errror Handling](#errror-handling)
- [Unrecoverable Erros With panic!](#unrecoverable-erros-with-panic)
- [Using a panic! Backtrace](#using-a-panic-backtrace)
- [Recoverable Error with Result](#recoverable-error-with-result)
- [Matching on Different Errors](#matching-on-different-errors)
- [Alternatives to Using match with Result\](#alternatives-to-using-match-with-resultt-e)
- [Shortcuts for Panic on Error: unwrap and expect](#shortcuts-for-panic-on-error-unwrap-and-expect)
- [Propagating Errors](#propagating-errors)
- [A Shortcut for Propagating Errors: the ? Operator](#a-shortcut-for-propagating-errors-the--operator)
- [Where The ? Operator Can Be Used](#where-the--operator-can-be-used)
- [To panic! or Not ro panic!](#to-panic-or-not-ro-panic)
- [Examples, Prototype Code, and Tests](#examples-prototype-code-and-tests)
- [Cases in Which You Have More Information Than the Compiler](#cases-in-which-you-have-more-information-than-the-compiler)
- [Guidelines for Error Handling](#guidelines-for-error-handling)
- [Creating Custom Types for Validation](#creating-custom-types-for-validation)
- [Generic Types, Traits, and Lifetimes](#generic-types-traits-and-lifetimes)
- [Removing Duplication by Extracting a Function](#removing-duplication-by-extracting-a-function)
- [Generic Data Types](#generic-data-types)
- [In Function Definitions](#in-function-definitions)
- [In Struct Definitions](#in-struct-definitions)
- [In Enum Definition](#in-enum-definition)
- [In Method Definition](#in-method-definition)
- [Performance of Code Using Generics](#performance-of-code-using-generics)
- [Trais: Defining Shared Behavior](#trais-defining-shared-behavior)
- [Defining a Trait](#defining-a-trait)
- [Implementing a Trait on a Type](#implementing-a-trait-on-a-type)
- [Default Implementations](#default-implementations)
- [Traits as Parameters](#traits-as-parameters)
- [Trait Bound Syntax](#trait-bound-syntax)
- [Specifying Multiple Trait Bounds with the + Syntax](#specifying-multiple-trait-bounds-with-the--syntax)
- [Clearer Trait Bounds with where Clauses](#clearer-trait-bounds-with-where-clauses)
- [Returning Types that Implement Traits](#returning-types-that-implement-traits)
- [Using Trait Bounds to Conditionally Implement Methods](#using-trait-bounds-to-conditionally-implement-methods)
- [Validating References with Lifetimes](#validating-references-with-lifetimes)
- [Preventing Dangling References with Lifetimes](#preventing-dangling-references-with-lifetimes)
- [The Borrow Checker](#the-borrow-checker)
- [Generic Lifetimes in Functions](#generic-lifetimes-in-functions)
- [Lifetime Annotation Syntax](#lifetime-annotation-syntax)
- [Lifetime Annotations in Function Signatures](#lifetime-annotations-in-function-signatures)
- [Thinking in Terms of Lifetimes](#thinking-in-terms-of-lifetimes)
- [Lifetime Annotations in Struct Definitions](#lifetime-annotations-in-struct-definitions)
- [Lifetime Elision](#lifetime-elision)
- [Lifetime Annotations in Method Definitions](#lifetime-annotations-in-method-definitions)
- [The Static Lifetime](#the-static-lifetime)
- [Generic Type Parameters, Trait Bounds, and Lifetimes Together](#generic-type-parameters-trait-bounds-and-lifetimes-together)
- [Writing Automated Tests](#writing-automated-tests)
- [How to Write Tests](#how-to-write-tests)
- [The Anatomy of a Test Function](#the-anatomy-of-a-test-function)
- [Checking Results with the assert! Macro](#checking-results-with-the-assert-macro)
- [Testing Equality with the assert\_eq! and assert\_ne! Macros](#testing-equality-with-the-assert_eq-and-assert_ne-macros)
- [Adding Custom Failure Messages](#adding-custom-failure-messages)
- [Checking for Panics with should\_panic](#checking-for-panics-with-should_panic)
- [Using Result\ in Tests](#using-resultt-e-in-tests)
- [Controlling How Tests Are Run](#controlling-how-tests-are-run)
- [Running Tests in Parallel or Consecutively](#running-tests-in-parallel-or-consecutively)
- [Showing Function Output](#showing-function-output)
- [Running a Subset of Tests by Name](#running-a-subset-of-tests-by-name)
- [Ignoring Some Tests Unless Specifically Requested](#ignoring-some-tests-unless-specifically-requested)
- [Test Organinzation](#test-organinzation)
- [Unit Tests](#unit-tests)
- [Testing Private Functions](#testing-private-functions)
- [Integration Tests](#integration-tests)
- [The tests Directory](#the-tests-directory)
- [Submodules in Integration Tests](#submodules-in-integration-tests)
- [Integration Tests for Binary Crates](#integration-tests-for-binary-crates)
- [An I/O Project: Building a Command Line Program](#an-io-project-building-a-command-line-program)
- [Accepting Command Line Arguments](#accepting-command-line-arguments)
- [Reading the Argument Values](#reading-the-argument-values)
- [Saving the Argument Values in Variables](#saving-the-argument-values-in-variables)
- [Reading a File](#reading-a-file)
- [Refactoring to Improve Modularity and Error Handling](#refactoring-to-improve-modularity-and-error-handling)
- [Separation of Concerns for Binary Projects](#separation-of-concerns-for-binary-projects)
- [Extracting the Argument Parser](#extracting-the-argument-parser)
- [Grouping Configuration Values](#grouping-configuration-values)
- [Creating a Constructor for Config](#creating-a-constructor-for-config)
- [Fixing the Error Handling](#fixing-the-error-handling)
- [Improving the Error Message](#improving-the-error-message)
- [Returning a Result Instead of Calling panic!](#returning-a-result-instead-of-calling-panic)
- [Calling Config::build and Handling Errors](#calling-configbuild-and-handling-errors)
- [Extracting Logic from main](#extracting-logic-from-main)
- [Returning Errors from the run Function](#returning-errors-from-the-run-function)
- [Handling Errors Returned from run in main](#handling-errors-returned-from-run-in-main)
- [Splitting Code into a Library Crate](#splitting-code-into-a-library-crate)
- [Developing the Library’s Functionality with Test-Driven Development](#developing-the-librarys-functionality-with-test-driven-development)
- [Writing a Failing Test](#writing-a-failing-test)
- [Writing Code to Pass the Test](#writing-code-to-pass-the-test)
- [Using the search Function in the run Function](#using-the-search-function-in-the-run-function)
- [Working with Environment Variables](#working-with-environment-variables)
- [Writing a Failing Test for the Case-Insensitive search Function](#writing-a-failing-test-for-the-case-insensitive-search-function)
- [Implementing the search\_case\_insensitive Function](#implementing-the-search_case_insensitive-function)
- [Writing Error Messages to Standard Error Instead of Standard Output](#writing-error-messages-to-standard-error-instead-of-standard-output)
- [Checking Where Errors Are Written](#checking-where-errors-are-written)
- [Printing Errors to Standard Error](#printing-errors-to-standard-error)
- [Functional Language Features: Iterators and Closures](#functional-language-features-iterators-and-closures)
- [Closures: Anonymous Functions that Can Capture Their Environment](#closures-anonymous-functions-that-can-capture-their-environment)
- [Capturing the Environment with Closures](#capturing-the-environment-with-closures)
- [Closure Type Inference and Annotation](#closure-type-inference-and-annotation)
- [Capturing References or Moving Ownership](#capturing-references-or-moving-ownership)
- [Moving Captured Values Out of Closures and the Fn Traits](#moving-captured-values-out-of-closures-and-the-fn-traits)
- [Processing a Series of Items with Iterators](#processing-a-series-of-items-with-iterators)
- [The Iterator Trait and the next Method](#the-iterator-trait-and-the-next-method)
- [Methods that Consume the Iterator](#methods-that-consume-the-iterator)
- [Methods that Produce Other Iterators](#methods-that-produce-other-iterators)
- [Using Closures that Capture Their Environment](#using-closures-that-capture-their-environment)
- [Improving Our I/O Project](#improving-our-io-project)
- [Removing a clone Using an Iterator](#removing-a-clone-using-an-iterator)
- [Using the Returned Iterator Directly](#using-the-returned-iterator-directly)
- [Using Iterator Trait Methods Instead of Indexing](#using-iterator-trait-methods-instead-of-indexing)
- [Making Code Clearer with Iterator Adaptors](#making-code-clearer-with-iterator-adaptors)
- [Choosing Between Loops or Iterators](#choosing-between-loops-or-iterators)
- [Comparing Performance: Loops vs. Iterators](#comparing-performance-loops-vs-iterators)
- [More about Cargo and Crates.io](#more-about-cargo-and-cratesio)
- [Customizing Builds with Release Profiles](#customizing-builds-with-release-profiles)
- [Publishing a Crate to Crates.io](#publishing-a-crate-to-cratesio)
- [Making Useful Documentation Comments](#making-useful-documentation-comments)
- [Commonly Used Sections](#commonly-used-sections)
- [Documentation Comments as Tests](#documentation-comments-as-tests)
- [Commenting Contained Items](#commenting-contained-items)
- [Exporting a Convenient Public API with pub use](#exporting-a-convenient-public-api-with-pub-use)
- [Setting Up a Crates.io Account](#setting-up-a-cratesio-account)
- [Adding Metadata to a New Crate](#adding-metadata-to-a-new-crate)
- [Publishing to Crates.io](#publishing-to-cratesio)
- [Publishing a New Version of an Existing Crate](#publishing-a-new-version-of-an-existing-crate)
- [Deprecating Versions from Crates.io with cargo yank](#deprecating-versions-from-cratesio-with-cargo-yank)
- [Cargo Workspaces](#cargo-workspaces)
- [Creating a Workspace](#creating-a-workspace)
- [Creating the Second Package in the Workspace](#creating-the-second-package-in-the-workspace)
- [Depending on an External Package in a Workspace](#depending-on-an-external-package-in-a-workspace)
- [Adding a Test to a Workspace](#adding-a-test-to-a-workspace)
- [Installing Binaries from Crates.io with cargo install](#installing-binaries-from-cratesio-with-cargo-install)
- [Extending Cargo with Custom Commands](#extending-cargo-with-custom-commands)
- [Smart Pointers](#smart-pointers)
- [Using \`Box to Point to Data on he Heap](#using-box-to-point-to-data-on-he-heap)
- [Using a Box to Store Data on the Heap](#using-a-box-to-store-data-on-the-heap)
- [Enabling Recursive Types with Boxes](#enabling-recursive-types-with-boxes)
- [More Information About the Cons List](#more-information-about-the-cons-list)
- [Computing the Size of a Non-Recursive Type](#computing-the-size-of-a-non-recursive-type)
- [Using Box to Get a Recursive Type with a Known Size](#using-box-to-get-a-recursive-type-with-a-known-size)
- [Treating Smart Pointers Like Regular References with the Deref Trait](#treating-smart-pointers-like-regular-references-with-the-deref-trait)
- [Following the Pointer to the Value](#following-the-pointer-to-the-value)
- [Using Box Like a Reference](#using-box-like-a-reference)
- [Defining Our Own Smart Pointer](#defining-our-own-smart-pointer)
- [Treating a Type Like a Reference by Implementing the Deref Trait](#treating-a-type-like-a-reference-by-implementing-the-deref-trait)
- [Implicit Deref Coercions with Functions and Methods](#implicit-deref-coercions-with-functions-and-methods)
- [How Deref Coercion Interacts with Mutability](#how-deref-coercion-interacts-with-mutability)
- [Running Code on Cleanup with the `Drop` Trait](#running-code-on-cleanup-with-the-drop-trait)
- [Dropping a Value Early with std::mem::drop](#dropping-a-value-early-with-stdmemdrop)
- [Rc, the Reference Counted Smart Pointer](#rc-the-reference-counted-smart-pointer)
- [Rc, the Reference Counted Smart Pointer](#rc-the-reference-counted-smart-pointer-1)
- [Using Rc to Share Data](#using-rc-to-share-data)
- [Cloning an Rc Increases the Reference Count](#cloning-an-rc-increases-the-reference-count)
- [RefCell and the Interior Mutability Pattern](#refcell-and-the-interior-mutability-pattern)
- [RefCell and the Interior Mutability Pattern](#refcell-and-the-interior-mutability-pattern-1)
- [Enforcing Borrowing Rules at Runtime with RefCell](#enforcing-borrowing-rules-at-runtime-with-refcell)
- [Interior Mutability: A Mutable Borrow to an Immutable Value](#interior-mutability-a-mutable-borrow-to-an-immutable-value)
- [A Use Case for Interior Mutability: Mock Objects](#a-use-case-for-interior-mutability-mock-objects)
- [Keeping Track of Borrows at Runtime with RefCell](#keeping-track-of-borrows-at-runtime-with-refcell)
- [Having Multiple Owners of Mutable Data by Combining Rc and RefCell](#having-multiple-owners-of-mutable-data-by-combining-rc-and-refcell)
- [Reference Cycles Can Leak Memory](#reference-cycles-can-leak-memory)
- [Creating a Reference Cycle](#creating-a-reference-cycle)
- [Preventing Reference Cycles: Turning an Rc into a Weak](#preventing-reference-cycles-turning-an-rc-into-a-weak)
- [Creating a Tree Data Structure: a Node with Child Nodes](#creating-a-tree-data-structure-a-node-with-child-nodes)
- [Adding a Reference from a Child to Its Parent](#adding-a-reference-from-a-child-to-its-parent)
- [Visualizing Changes to strong\_count and weak\_count](#visualizing-changes-to-strong_count-and-weak_count)
- [Fearless Concurrency](#fearless-concurrency)
- [Using Threads to Run Code Simultaneously](#using-threads-to-run-code-simultaneously)
- [Creating a New Thread with spawn](#creating-a-new-thread-with-spawn)
- [Waiting for All Threads to Finish Using join Handles](#waiting-for-all-threads-to-finish-using-join-handles)
- [Using move Closures with Threads](#using-move-closures-with-threads)
- [Using Message Passing to Transfer Data Between Threads](#using-message-passing-to-transfer-data-between-threads)
- [Channels and Ownership Transference](#channels-and-ownership-transference)
- [Sending Multiple Values and Seeing the Receiver Waiting](#sending-multiple-values-and-seeing-the-receiver-waiting)
- [Creating Multiple Producers by Cloning the Transmitter](#creating-multiple-producers-by-cloning-the-transmitter)
- [Shared-State Concurrency](#shared-state-concurrency)
- [Using Mutexes to Allow Access to Data from One Thread at a Time](#using-mutexes-to-allow-access-to-data-from-one-thread-at-a-time)
- [The API of Mutex](#the-api-of-mutex)
- [Sharing a Mutex Between Multiple Threads](#sharing-a-mutex-between-multiple-threads)
- [Multiple Ownership with Multiple Threads](#multiple-ownership-with-multiple-threads)
- [Atomic Reference Counting with Arc](#atomic-reference-counting-with-arc)
- [Similarities Between RefCell/Rc and Mutex/Arc](#similarities-between-refcellrc-and-mutexarc)
- [Extensible Concurrency with the Sync and Send Traits](#extensible-concurrency-with-the-sync-and-send-traits)
- [Allowing Transference of Ownership Between Threads with Send](#allowing-transference-of-ownership-between-threads-with-send)
- [Allowing Access from Multiple Threads with Sync](#allowing-access-from-multiple-threads-with-sync)
- [Implementing Send and Sync Manually Is Unsafe](#implementing-send-and-sync-manually-is-unsafe)
- [Object Oriented Programming Features of Rust](#object-oriented-programming-features-of-rust)
- [Characteristics of Object-Oriented Languages](#characteristics-of-object-oriented-languages)
- [Objects Contain Data and Behavior](#objects-contain-data-and-behavior)
- [Encapsulation that Hides Implementation Details](#encapsulation-that-hides-implementation-details)
- [Inheritance as a Type System and as Code Sharing](#inheritance-as-a-type-system-and-as-code-sharing)
- [Using Trait Objects that Allow for Values of Different Types](#using-trait-objects-that-allow-for-values-of-different-types)
- [Defining a Trait for Common Behavior](#defining-a-trait-for-common-behavior)
- [Implementing the Trait](#implementing-the-trait)
- [Trait Objects Perform Dynamic Dispatch](#trait-objects-perform-dynamic-dispatch)
- [Implementing an Object-Oriented Design Pattern](#implementing-an-object-oriented-design-pattern)
- [Defining Post and Creating a New Instance in the Draft State](#defining-post-and-creating-a-new-instance-in-the-draft-state)
- [Storing the Text of the Post Content](#storing-the-text-of-the-post-content)
- [Ensuring the Content of a Draft Post Is Empty](#ensuring-the-content-of-a-draft-post-is-empty)
- [Requesting a Review of the Post Changes Its State](#requesting-a-review-of-the-post-changes-its-state)
- [Adding approve to Change the Behavior of content](#adding-approve-to-change-the-behavior-of-content)
- [Trade-offs of the State Pattern](#trade-offs-of-the-state-pattern)
- [Encoding States and Behavior as Types](#encoding-states-and-behavior-as-types)
- [Implementing Transitions as Transformations into Different Types](#implementing-transitions-as-transformations-into-different-types)
- [Patterns and Matching](#patterns-and-matching)
- [All the Places Patterns Can Be Used](#all-the-places-patterns-can-be-used)
- [match Arms](#match-arms)
- [All the Places Patterns Can be Used](#all-the-places-patterns-can-be-used-1)
- [Conditional `if let` Expressions](#conditional-if-let-expressions)
- [`while let` Conditional Loops](#while-let-conditional-loops)
- [`for` Loops](#for-loops)
- [`let` Statements](#let-statements)
- [Function Parameters](#function-parameters)
- [Refutability: Whether a Pattern Might Fail to Match](#refutability-whether-a-pattern-might-fail-to-match)
- [Pattern Syntax](#pattern-syntax)
- [Matching Literals](#matching-literals)
- [Matching Named Variables](#matching-named-variables)
- [Multiple Patterns](#multiple-patterns)
- [Matching Ranges of Values with ..=](#matching-ranges-of-values-with-)
- [Destructuring to Break Apart Values](#destructuring-to-break-apart-values)
- [Destructuring Structs](#destructuring-structs)
- [Destructuring Enums](#destructuring-enums)
- [Destructuring Nested Structs and Enums](#destructuring-nested-structs-and-enums)
- [Destructuring Structs and Tuples](#destructuring-structs-and-tuples)
- [Ignoring Values in a Pattern](#ignoring-values-in-a-pattern)
- [Ignoring an Entire Value with \_](#ignoring-an-entire-value-with-_)
- [Ignoring Parts of a Value with a Nested \_](#ignoring-parts-of-a-value-with-a-nested-_)
- [Ignoring an Unused Variable by Starting Its Name with \_](#ignoring-an-unused-variable-by-starting-its-name-with-_)
- [Ignoring Remaining Parts of a Value with ..](#ignoring-remaining-parts-of-a-value-with-)
- [Extra Conditionals with Match Guards](#extra-conditionals-with-match-guards)
- [@ Bindings](#-bindings)
- [Advanced Features](#advanced-features)
- [Unsafe Rust](#unsafe-rust)
- [Unsafe Superpowers](#unsafe-superpowers)
- [Dereferencing a Raw Pointer](#dereferencing-a-raw-pointer)
- [Calling an Unsafe Function or Method](#calling-an-unsafe-function-or-method)
- [Creating a Safe Abstraction over Unsafe Code](#creating-a-safe-abstraction-over-unsafe-code)
- [Using extern Functions to Call External Code](#using-extern-functions-to-call-external-code)
- [Calling Rust Functions from Other Languages](#calling-rust-functions-from-other-languages)
- [Accessing or Modifying a Mutable Static Variable](#accessing-or-modifying-a-mutable-static-variable)
- [Implementing an Unsafe Trait](#implementing-an-unsafe-trait)
- [Accessing Fields of a Union](#accessing-fields-of-a-union)
- [When to Use Unsafe Code](#when-to-use-unsafe-code)
- [Advanced Traits](#advanced-traits)
- [Specifying Placeholder Types in Trait Definitions with Associated Types](#specifying-placeholder-types-in-trait-definitions-with-associated-types)
- [Default Generic Type Parameters and Operator Overloading](#default-generic-type-parameters-and-operator-overloading)
- [Fully Qualified Syntax for Disambiguation: Calling Methods with the Same Name](#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name)
- [Using Supertraits to Require One Trait’s Functionality Within Another Trait](#using-supertraits-to-require-one-traits-functionality-within-another-trait)
- [Using the Newtype Pattern to Implement External Traits on External Types](#using-the-newtype-pattern-to-implement-external-traits-on-external-types)
- [Advanced Types](#advanced-types)
- [Using the Newtype Pattern for Type Safety and Abstraction](#using-the-newtype-pattern-for-type-safety-and-abstraction)
- [Creating Type Synonyms with Type Aliases](#creating-type-synonyms-with-type-aliases)
- [The Never Type that Never Returns](#the-never-type-that-never-returns)
- [Dynamically Sized Types(DST) and the Sized Trait](#dynamically-sized-typesdst-and-the-sized-trait)
- [Advanced Functions \& Closures](#advanced-functions--closures)
- [Function Pointers](#function-pointers)
- [Returning Closures](#returning-closures)
- [Macros](#macros)
- [The Difference Between Macros and Functions](#the-difference-between-macros-and-functions)
- [Declarative Macros with macro\_rules! for General Metaprogramming](#declarative-macros-with-macro_rules-for-general-metaprogramming)
- [Procedural Macros for Generating Code from Attributes](#procedural-macros-for-generating-code-from-attributes)
- [How to Write a Custom derive Macro](#how-to-write-a-custom-derive-macro)
- [Attribute-like macros](#attribute-like-macros)
- [Function-like macros](#function-like-macros)
- [Final Project: Building a Multithreaded Web Server](#final-project-building-a-multithreaded-web-server)
- [Building a Single-Threaded Web Server](#building-a-single-threaded-web-server)
- [Listening to the TCP Connection](#listening-to-the-tcp-connection)
- [Reading the Request](#reading-the-request)
- [A Closer Look at an HTTP Request](#a-closer-look-at-an-http-request)
- [Writing a Response](#writing-a-response)
- [Returning Real HTML](#returning-real-html)
- [Validating the Request and Selectively Responding](#validating-the-request-and-selectively-responding)
- [A Touch of Refactoring](#a-touch-of-refactoring)
- [Turning Our Single-Threaded Server into a Multithreaded Server](#turning-our-single-threaded-server-into-a-multithreaded-server)
- [Simulating a Slow Request in the Current Server Implementation](#simulating-a-slow-request-in-the-current-server-implementation)
- [Improving Throughput with a Thread Pool](#improving-throughput-with-a-thread-pool)
- [Spawning a Thread for Each Request](#spawning-a-thread-for-each-request)
- [Creating a Finite Number of Threads](#creating-a-finite-number-of-threads)
- [Building ThreadPool Using Compiler Driven Development](#building-threadpool-using-compiler-driven-development)
- [Validating the Number of Threads in new](#validating-the-number-of-threads-in-new)
- [Creating Space to Store the Threads](#creating-space-to-store-the-threads)
- [A Worker Struct Responsible for Sending Code from the ThreadPool to a Thread](#a-worker-struct-responsible-for-sending-code-from-the-threadpool-to-a-thread)
- [Sending Requests to Threads via Channels](#sending-requests-to-threads-via-channels)
- [Implementing the execute Method](#implementing-the-execute-method)
- [Graceful Shutdown and Cleanup](#graceful-shutdown-and-cleanup)
- [Implementing the Drop Trait on ThreadPool](#implementing-the-drop-trait-on-threadpool)
- [Signaling to the Threads to Stop Listening for Jobs](#signaling-to-the-threads-to-stop-listening-for-jobs)
# [The Rust Programming Language](https://doc.rust-lang.org/book/) Summary
> Summary, Short version of The Rust Programming Language.
## Version
- Updated on 2023-02 with Rust 1.65 over version aka 2021 edition
# Foreword
- Rust empowers you to reach farther, to program with confidence in a wider variety of domains then did before
- Not only low-level systems programming, ergonomic enough to make CLI apps, web servers, web app, Raspberry Pi, and many other kinds of code quite pleasant to write
# Introduction
## Who Rust Is For
### Teams of Developers
Rust is proving to be a productive tools for collaborating among large teams of developer
- Cargo, dependency manager and build tool
- Rustfmt, formatting tools to ensure a consistent coding style
- The Rust Language Server for IDE integrations for code completion and iline error message
### Students
- For students who are interested in learning about system concepts
- The community is very welcoming and happy to answer student questions
### Companies
- Hundreds of companies, large and small, use Rust in production for a variety of tasks
- CLI tools, web services, DevOps tooling, embedded devices, audio and video analysis and transcoding, cryptocurrencies, bioinformatics, search engines, Internet of Things applications, machine learning, and even major parts of the Firefox web browser
### Open Source Developers
- For people who want to build the Rust programming language, community, developer tools, and libraries
### People Who Value Speed and Stability
- For people who crave speed and stability in a language
- The Rust compiler’s checks ensure stability through feature additions and refactoring
- By striving for zero-cost abstractions, higher-level features that compile to lower-level code as fast as code written manually
- Rust’s greatest ambition is to eliminate the trade-offs that programmers have accepted for decades by providing safety and productivity, speed and ergonomics
# Getting Started
## Installation
- Run `curl https://sh.rustup.rs -sSf | sh` to download `rustup`
- `rustup`: command line tool for managing Rust version and tools
- Add the following line to your env `export PATH="$HOME/.cargo/bin:$PATH"`
- If installation is already done, run `rustup update`
- To uninstall, `rustup self uninstall`
- On macOS, we need xcode command line tools by using `xcode-select --install`
## Hello, World!
- Create a project directory
```sh
mkdir hello_world
cd hello_world
```
- New open the main.rs file then save with belows:
```rust
// main.rs
fn main() {
println!("Hello, world!"); // `!` is macro. end with ';'
}
```
- Compile main.rs with `rustc`
```sh
$ rustc main.rs
$ ./main
```
## Hello, Cargo
- Cargo is Rust's build system and package manager
- `cargo new $PROJECT_NAME` to create a project
- `-—bin` extra option for executable binary
- `-—lib` extra option for library
- carg ogenerate Cargo.toml in TOML format
```rust
[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2021"
[dependencies]
```
- Building and running a Cargo projects
```sh
# building
$ cargo build
# running
$ ./target/debug/hello_cargo
or
$ cargo run
# checking before build
cargo check
# update crates
cargo update
```
# Programming a Guessing Game
Hand-on project, you will lean about let, match, methods, asscicated function, external crates and more
## Setting Up a New Project
- Create a new project and run
```sh
$ cargo new guessing_game --bin
$ cargo ru
```
# Processing a Guess
```rust
use std::io;
use std::cmp::Ordering;
// use external crate rand, see Cargo.toml
use rand::Rng;
fn main() {
// print message
println!("Guess the number!");
// gen_range is de
let secret_number = rand::thread_rng().gen_range(1..=101);
println!("the serect number is: {}", secret_number);
loop {
println!("Please input your guess.");
// immutable variable to save user input
let mut guess = String::new();
io::stdin()
// read stdio buffer and write it to guess
.read_line(&mut guess)
.expect("Failed to read line");
// handling invalid input
let guess: u32 = match guess.trim().parse() {
// convert to u32
Ok(num) => num,
Err(_) => continue,
};
println!("You guessed: {}", guess);
// match expression to compare guess and secret_number
match guess.cmp(&secret_number) {
// Ordering type is another enum, Less, Greater, Equal
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
// quite the loop
break;
}
}
}
}
```
# Common Programming Concepts
> This chapter covers concepts that appear in almost every programming language and how they work in Rust.
## Variables and Mutability
- By default, variables are immutable
- Using `mut` to make a variable as mutable
## Differences Between Variable and Constants
- No `mut` for constants
- Constants can be declared in any scope
- Set with constant expression
## Shadowing
- Without `let` we will get compile error
```rust
fn main() {
let x = 5;
let x = x + 1;
let x = x * 2;
// (5 + 1) * 2 = 12
println!("The value of x is: {}", x);
}
```
- We can change the type, reuse the same name
```rust
// reuse the variable with the another type
let spaces = " "; // string type
let spaces = spaces.len(); // bind new type value
// compile error with using mut
let mut spaces = " ";
spaces = spaces.len();
```
## Data Types
### Scalar Types
Single value: integers, floating-point numbers, Booleans, and characters
- Integers Types: i/u8, i/u16, i/u32, i/u64, i/u128, i/usize
- Intergers literals:
- Decimal: `98_222` // 98.222
- Hex: 0xff
- Octal: 0o77
- Binary?: `0b1111_0000` // 11110000
- Byte(u8):` b'A'`
- u8: `57u8`
- Overflow, in debug, it panic, in production, wrapping
- Floating-Point: f64, default, same speeed as f32, double precision. f32, single precision
- Numberic Operations: `+, -, *, / , %`
- The Boolean Type: `true, false`
- The Character Type: unicode scalar values range from U+0000 to u+D7FFF and U+E000 to U+10FFFF
- `char`
- "string literal"
### Compound Types
- Group multiple values into one type, tuple and arrays
- The Tuple Type, fixed length, once declared, cannot grow or shirnk in size
```rust
// decalration
let tup: (i32, f64, u8) = (500, 6.4, 1);
// access with index
let five_hundred = tup.0; // start with 0
let six_point_four = tup.1;
let one = tup.2;
// destructure
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
```
- The Array Type, collection of multiple values with the same type, fixed length unlike other languages.
```rust
// declaration
let a = [1, 2, 3, 4, 5];
let b: [i32; 5] = [1, 2, 3, 4, 5];
// accessing
let first = a[0];
// out of indexing, rust protects this kind of error from memory corruption
let index = 10;
let run_time_error = a[index];
```
## Functions
- Using snake case
- Defining function anywhere
### Parameters
```rust
fn another_function(x) {
println!("The value of x is: {}", x);
}
fn another_function(y: i32) {
println!("The value of y is: {}", y);
}
```
### Statements and Expressions
```rust
fn statement_fn() {
// Statements are instructions that perform some action and do not return a value
println!("function!");
}
fn main() {
let x = 5;
let y = { // Expressions evaluate to a resultant value
let x = 3;
x + 1 // no semicolun at the end of the expression
}
println!("y is {}", y); // y is 4
}
```
### Functions with Return Values
```rust
fn five() -> i32 {
5 // end of expression without return
}
fn plus_one(x: i32) -> i32 {
x + 1
}
```
## Comments
- Commenting with two slashes and continue until the end of line
## Control Flow
### if expression
```rust
let number = 3;
if number < 5 {
println!("number is under 5");
} else {
println!("number isnot under 5");
}
// error, missmatch type, expected `bool`, found integer
// must be `number != 0`
if number {
...
}
// handling multiple if
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
// if in a let statement
let condition = true;
// must be return same type variable
let number = if confition { 5 } else { 6 };
```
### Repeatition with Loops
- Returning Values from Loops
```rust
fn main() {
let mut counter = 0;
let result = loop { // loop
counter += 1;
println!("again!");
if counter == 10 {
// return value from loop
break counter * 2 // return 10 * 2
}
}
println!("The result is {result}"); // The result is 20
}
```
- Loop Labels to Disambiguate Between Multiple Loops
```rust
fn main() {
let mut count = 0;
'counting_up: loop {
println!("count = {count}");
let mut remaining = 10;
loop {
println!("remaining = {remaining}");
if remaining == 9 {
break;
}
if count == 2 {
// break parent loop,
break 'counting_up;
}
remaining -= 1;
}
count += 1;
}
println!("End count = {count}");
}
```
- Contitional Loops with while
```rust
fn main() {
let mut number = 3;
while number != 0 {
println!("again!");
number = number - 1;
}
}
```
- Looping Through a Collection with for
```rust
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a {
println!("val is {}", element);
}
for number in (1..4).rev() {
println!("val is {}", number);
}
}
```
# Understanding Ownership
- Enables Rust to make memory safety guarantees without needing a `garbage collector`.
- In Ruet, memory is managed through a system of ownership `with a set of rules that compiler checks at compile time`
## What is Ownership?
- Ownership is a set of rules that govern how a Rust program manages memory
- Rust uses a third approach: memory is managed through a system of ownership with a set of rules that the compiler checks
### Ownership Rules
- Each value in Rust has an owner
- There can only be one owner at a time
- When the owner goes out of scope, the value will be dopped
### Variable Scope
```rust
{ // s is not valid here, it's not yet declared
let s = "hello"; // s is valid from this point forward
// do stuff with s
} // this scope is over, and s is no longer valid
```
### The String Type
> Rust has a second string type, String. This type manages data allocated on the heap and as such is able to store an amount of text that is unknown to us at compile time
```rust
let s = String::from("hello"); // string is allocated on the heap, String:: is namespace
let mut s2 = String::from("hello");
s2.push_str(", world!");
```
### Memory and Allocation
- Rust takes a different path: the memory is automatically returned once the variable that owns it goes out of scope
- When a variable goes out of scope, Rust calls a special function for us. This function is called `drop`
- Rust calls `drop` automatically at the closing curly bracket
```rust
{
let s = String::from("hello"); // s is valid from this point forward
...
} // this scope is now over, s goes out of scope, and s is no longer valid
```
### Ways Variables and Data Interact: Move
```rust
// these two 5 values are pused to onto the stack
let x = 5;
let y = x;
// two of containers for the string data (ptr, len, and capacity)
let s1 = String::from("hello");
let s2 = s1; // s1 string data is moved to s2, Rust invalidates the first variable, instead of being called a shallow copy
println!("{}, world!", s1); // error, string data has been moved to s2
```
- Rust will never automatically create “deep” copies of your data. Therefore, `any automatic copying` can be assumed to be inexpensive in terms of runtime performance.
### Ways Variables and Data Interact: Clone
```rust
let s1 = String::from("hello");
let s2 = s1.clone(); // using deep copy to call clone()
println!("s1 = {}, s2 = {}", s1, s2);
```
### Stack-Only Data: Copy
- Types such as integers that have a known size at compile time are stored entirely on the stack, so copies of the actual values are quick to make.
```rust
let x = 5;
let y = x; // x is stil valid after x moved to y
println!("x = {}, y = {}", x, y)
```
- Rust has a `special annotation called the Copy` trait that we can place on types that are stored on the stack, as integers are
- Rust won’t let us annotate a type with Copy if the type, or any of its parts, has implemented the `Drop trait`.
- Any group of simple scalar values can implement `Copy`, some of the types that implement Copy:
- All the integer types, such as `u32`
- The Boolean type `bool`, with values `true` and `false`
- All the floating point type, such as `f64`
- The character type `char`
- Tuples, if they only contain types that are also `Copy`. For example, `(i32, i32)` is `Copy` but `(i32, string)` is not
### Ownership and Function
- Passing a variable to a function will `move or copy`, just as assgiment does
```rust
fn main() {
let s = String::from("hello"); // s comes into scope
takes_ownership(s); // s's value moves into the function
// s is no longer valid
let x = 5; // x comes into scope
makes_copy(x); // x would move into the function
// but i32 is Copy, so it's ok to use x afterward
}
fn takes_ownership(some_string: String) { // some_string comes into scope
println!("{}", some_string);
} // some_string goes out of scope and `drop`, `the backing memory is freed`
fn makes_copy(some_integer: i32) { // some_integer comes into scope
println!("{}", some_integer);
} // some_integer goes out of scope. `Nothing special happens.`
```
### Return Values and Scope
- Returning values can also transfer ownership
```rust
fn main() {
let s1 = gives_ownership(); // gives_ownership moves its return value into s1
let s2 = String::from("hello"); // s2 comes into scope
let s3 = takes_and_gives_back(s2); // s2 is moved into takes_and_gives_back
// takes_and_gives_back moves its return value into s3
} // s3 goes out of scope, and is dropped
// s2 goes out of scope, but was moved, so nothing happens
// s1 goes out of scope, and it dropped
fn gives_ownership() -> String { // gives_ownership will move its return value into the function that calls it
let some_string = String::from("hello"); // some_string comes into scope
some_string // some_string is returned and moves out to the calling function
}
fn takes_and_givs_back(a_string: String) -> String { // a_string comes into scope
a_string // a_string is returned and moves out to the calling function
}
```
- The ownership of a variable follows the same pattern every time
- Assigning a value to another variable moves it. When a variable that includes data on the heap goes out of scope
- The value will be cleaned up by drop unless ownership of the data has been moved to another variable.
### Returning with Tuple
```rust
fn main() {
let s1 = String::from("hello");
let (s2, len) = calculate_length(s1);
println!("The length of '{}' is {}.", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len();
(s, length)
}
```
## References and Borrowing
```rust
fn main() {
let s1 = String::from("hello"); // s1 will not be dropped when the reference goes out of scope
let len = calculate_length(&s1); // s1 still have ownership because provide its as reference
println!("{}, {}", s1, len); // the tuple is stil valid
}
fn calulate_length(s: &String) -> usize { // s is a reference
s.len()
} // goes out of scope, it refer to, it is not dropped because s doesn't have ownership
```
### Mutable References
```rust
fn main() {
let mut s = String::from("hello");
change(&mut s); // updatable reference
cannot_change(&s);
}
fn canot_change(some_string: &String) { // cannot borrow immutable borrowed content
some_string.push_ptr(", world"); // ERROR!
}
fn change(some_string: &mut String) {
some_string.push_ptr(", world");
}
```
#### Mutable refernces restriction
you can only have `one mutable reference` to a particular piece of data in particular scope. The benefit of having this restriction is that Rust can prevent data races at compile time. This code will fail:
```rust
let mut s = String:from("hello");
let r1 = &mut s;
let r2 = &mut s; // s cannot be borrowed more than once at a time
println!("{}, {}", r1, r2); // first borrow later used here
```
#### Multiple mutable refernes by creating a new scope
```rust
let mut s = String:from("hello");
{
let r1 = &mut s;
} // r1 goes out of scope here, now we can make a new reference
let r2 = &mut s;
```
#### Immutable references
Cannot have a mutable reference while we have an immutable one.
```rust
let mut s = String::from("hello");
let r1 = &s; // no problem, immutable borrow occurs here
let r2 = &s; // no problem
let r3 = &mut s; // ERROR, mutable borrow occurs here
```
These scopes don’t overlap, so this code is allowed.
```rust
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
println!("{} and {}", r1, r2);
// variables r1 and r2 will not be used after this point
let r3 = &mut s; // no problem
println!("{}", r3);
```
### Dangling References
> dangling pointer—a pointer that references a location in memory that may have been given to someone else
In Rust, the compiler guarantees that references will never be dangling reference
```rust
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String { // dangle returns a reference to a String
let s = String::from("hello"); // s is a new String in dangle
&s // ERROR! missing lifetime specifier
} // s goes out of scope, and is dropped. It's memory goes away
fn no_dangle() -> String {
let s = String::from("hello");
s
}
```
## The Slice Type
- Slice let you reference a contiguous sequence of elements in a colleaction rather than the whole collection
- Slice doesn't have ownership
```rust
fn first_word(s: &String) -> usize {
let bytes = s.as_bytes();
// use reference
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return i;
}
}
s.len()
}
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s); // word will get the value 5
s.clear(); // empties the String, making it euqal to ""
// word still has the value 5 here, but there's no more string that
// we could meaningfully use the value 5 with. word is now totally invalid!
}
```
### String Slices
- String slice is a `reference` to part of a String
```rust
let s = String::from("hello");
// with ..
let slice = &s[0..2]; // => hel
let slice = &s[..2]; // => hel
// drop the trailing number
let let = s.len();
let slice = &s[3..len]; // => o
let slice = &s[3..]; // => o
// both drop
let slice = &s[0..len]; // => hello
let slice = &s[..]; // => hello
```
- Rewrite of `first_word`
```rust
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if (item == b' ') {
return &s[0..i];
}
}
&s[..]
}
// If we have an immutable reference to something, we `cannot` also take a mutable reference
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s); // immutable borrow occurs here
s.clear(); // error!, mutable borrow occrus here
println!("the first word is: {}", word); // word is still used here
}
```
#### String literals are slices
String literals being stored inside the binary.
```rust
let s = "Hello, world!"; // s is &str, slice pointing, immutable reference
```
#### String Slices as Paramters
We can pass string slice directly or slice of the entire String
```rust
fn first_word(s: &str) -> &str { // support &String and &str (deref)
...
}
fn main() {
let my_string = String::from("hello world");
// `first_word` works on slices of `String`s, whether partial or whole
let word = first_word(&my_string[0..6]);
let word = first_word(&my_string[..]);
// `first_word` also works on references to `String`s, which are equivalent
// to whole slices of `String`s
let word = first_word(&my_string);
let my_string_literal = "hello world";
// `first_word` works on slices of string literals, whether partial or whole
let word = first_word(&my_string_literal[0..6]);
let word = first_word(&my_string_literal[..]);
// Because string literals *are* string slices already,
// this works too, without the slice syntax!
let word = first_word(my_string_literal);
}
```
#### Other slices
The slice has the type `&[i32]`, works the same way as string slice do
```rust
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3];
```
# Using Structs to Structre Related Data
> Structure, is a custom data type that lets you package together and name multiple related values that make up a meaningful group
## Defining and Instantiation Structs
- The pieces of a struct can be different type, can be named
```rust
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool
}
```
- Create an instance by specifying concreat values
```rust
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someone"),
active: true,
sign_in_count: 1,
}
```
- Get value with `dot notation`. it the instance is mutable, we can change a value by using the dot notation
- Certain field as mutable is not allowed
```rust
let mut user1 = User {
email: String::from("someone@example.com"),
username: String::from("someone"),
active: true,
sign_in_count: 1,
}
user1.email = String::from("someone@example.com");
```
- Using builder function
```rust
// implicity return that new instance
fn build_user(email: String, username: String) -> User {
User {
email: email,
username: username,
active: true,
sign_in_count: 1,
}
}
```
### Using the Field Init Shorthand
```rust
fn build_user(email: String, username: String) -> User {
email, // no repetition
username,
active: true,
sign_in_count: 1,
}
```
### Creating instances from other instnace with update value, or struct update syntax
```rust
// without update syntax
let user2 = User {
email: String::from("another@example.com"),
username: String::from("another"),
active: user1.active,
sign_in_count: user1.sign_in_count
};
// using struct update syntax `..`
let user2 = User {
email: String::from("another@example.com"),
username: String::from("another"),
..user1
}
```
### Using Tuple Structs Without Named Fields to Create Different Types
```rust
struct Color(i32, i32, i32); // Tuple structs
struct Point(i32, i32, i32);
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
```
### Unit-like Structs without Any Field
```rust
struct AlwaysEqual;
fn main() {
let subject = AlwaysEqual;
}
```
## An Example Program Using Struct
```rust
// sample code with multiple arguments
fn area(width: u32, height: u32) -> u32 {
width * height
}
area(width, height);
```
### Refactoring with Tuples
```rust
fn area(dimensions: (u32, u32)) -> u32 {
return dimensions.0 * dimensions.1
}
area((30, 50));
```
### Refactoring with Structs: Adding More Meaning
```rust
// with struct
struct Rectangle {
width: u32,
height: u32
}
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
let rect = Rectangle { width: 30, height: 50 };
area(&rect);
```
### Adding Useful Functionality with Derived Traits
```rust
#[derive(Debug)] // define outer attribute
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!("rect 1 is {:?}", rect1); // {} doesn't work because of lack of Display implement
let rect2 = Rectangle {
width: dbg!(30 * scale), // dbg! macro to prints the stderr
height: 50
};
dbg!(&rect2);
}
```
## Method Syntax
> Methods are similar to functions, methods are different from functions in that they're defined within the context of a struct
### Defining Methods
```rust
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
// starts with impl
impl Rectangle {
// &self instead of rectangle: &Rectangle
// `&mut self`, to take an ownership
fn area(&self) -> u32 {
self.width * self.height
}
// same name as one of the struct's fields
fn width(&self) -> bool {
self.width > 0
}
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
)
}
```
### Where’s the -> Operator?
```rust
// Rust has a feature called automatic referencing and dereferencing, automatically adds in `&, &mut or *` instead of using `->`. following are same
p1.distance(&p2);
(&p1).distance(&p2);
```
### Methods with More Parameters
```rust
impl Rectangle {
fn area(&self) -> u32 {...}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
...
rect1.can_hold(&rect2);
```
### Associated Functions
```rust
impl Rectangle {
// Assoociated functions, without `self` parameter, still functions, not methods
fn square(size: u32) -> Rectangle {
// Often used for constructor that will return a new instance of the struct
Rectangle { width: size, height: size }
}
}
let sq = Rectangle::square(3);
```
### Multiple impl Blocks
```rust
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
```
# Enums and Pattern Matching
## Defining an Enum
````rust
// define enum without type
enum IpAddrKind {
V4,
V6,
}
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
fn route(ip_type: IpAddrKind) {...}
struct IpAddr {
kind: IpADdrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
}
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
}
// define enum value associated String
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::From("127.0.0.1"));
let loopback = IpAddr::V6(String::From("::1"));
// define enum with different types and amounts of associated data
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(String::From(127, 0, 0, 1));
let loopback = IpAddr::V6(String::From("::1"));
// define enum embedded struct as associated type
struct Ipv4Addr {
...
}
struct Ipv6Addr {
...
}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr)
}
// define enum with variant
enum Message {
Quit,
Move { x:i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32)
}
// define method using impl
impl Message {
fn call(&self) {
...
}
}
let m = Message.Write(String::from("hello"));
m.call();
````
## The `Option` Enum and Its Advantages Over Null Values
- Rust does not have nulls, but it does have an enum that being present of absent
- Another enum, compiler can check whether you've handled all the cases
- Option is defined by the standard library
- Option value, `Some`, contains a value, or `None`, does not
```rust
// Option is defined by the standard libaray, included in the prelude
enum Option {
None,
Some(T),
}
// hold number type
let some_number = Some(5);
// hold string type
let some_string = Some("a string")
// hold none value
let absent_number: Option = None;
let x: i8 = 5;
let y: Options = Some(5);
let sum = x + y; // error, they are different type, i8 + std::option:Option
```
### [Use Some Value with various method](https://doc.rust-lang.org/std/option/enum.Option.html)
```rust
let mut x = Some(2);
match x.as_mut() {
Some(v) => *v = 42, // matched
None => {},
}
assert_eq!(x, Some(42)); // valid
let x = Some("value");
assert_eq!(x.expect("the world is ending"), "value"); // valid, expect return some value, but if some value is None? panics with "the world is ending"
let x: Option = Some(2);
assert_eq!(x.is_some(), true); // valid
let x: Option = None;
assert_eq!(x.is_some(), false); // valid
let x: Option = Some(2);
assert_eq!(x.is_none(), false); // valid
let x: Option = None;
assert_eq!(x.is_none(), true); // valid
```
### Some Value usecases
```rust
fn divide(numerator: f64, denominator: f64) -> Option {
if denominator == 0.0 {
None
} else {
Some(numerator / denominator)
}
}
// The return value of the function is an option
let result = divide(2.0, 3.0);
// Pattern match to retrieve the value
match result {
// The division was valid
Some(x) => println!("Result: {}", x), // 0.6666666666666666
// The division was invalid
None => println!("Cannot divide by 0"),
}
```
## The `match` Control Flow Operator
- Basic usese
```rust
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
```
- Run multiple lines of code in a match arm
```rust
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
println!("Lucky penny!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
```
### Patterns That Bind to Values
```rust
#[derive(Debug)] // so we can inspect the state in a minute
enum UsState {
Alabama,
Alaska,
// --snip--
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn value_in_cents(coin: Coin) -> u32 {
match coin {
// match enum and return function
Coin::Peny => {
println!("Lucky penny!");
1
}
// match enum and return value
Coin::Nickel => 5,
Coin::Dime => 10,
// patterns that bind to values, value_in_cents(Coin::Quarter(UsState::Alaska))
Coin::Quater(state) => {
println("State quater from{:?}!", state);
25
},
}
}
```
### Matching with `Option`
```rust
fn plus_one(x: Option) -> Option {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five); // returns 6
let none = plus_one(None); // No value to add to, stops and return `None`
```
### Matches Are Exhaustive
The arms’ patterns must cover all possibilities
```rust
fn plus_one(x: Option) -> Option {
match x {
Some(i) => Some(i + 1), // Error, didn't cover every possible case, `Pattern 'None` not covered
}
}
```
### Catch-all Patterns and the _ Placeholder
Use this when we don't want to list all possible values. `_` pattern will match any value.
```rust
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => reroll(),cases
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn reroll() {}
}
```
We can express that by using the empty tuple type as the code that goes with the _ arm
```rust
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => (),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
```
## Concise Control FLow with `if let`
- `if let` syntax lets you combine `if` and `let` into a less verbos way to handle value that match one pattern while ignoring the rest
### Match one pattern
`if let` is `syntax sugar` for a match that runs code when the value matches one pattern
```rust
let some_u8_value = Some(0u8);
match some_u8_value {
Some(3) => println!("three"),
_ => (),
}
// only work woth Some(3), using if let instead of
if let Some(3) = some_u8_value {
println!("three");
}
```
### With else
```rust
fn main() {
let mut count = 0;
if let Coin::Quater(state) = coin {
println!("State quater from {:?}!", state);
} else {
count + 1;
}
}
```
# Managing Growing Projects with Packages, Crates, and Modules
As you write large programs, organizing your code will become increasingly important. By grouping related functionality and separating code with distinct features, you’ll clarify where to find code that implements a particular feature and where to go to change how a feature works.
- Packages: A Cargo feature that let you build, test, and share crates
- Crates: A tree of modules that produce a library or executable
- Modules and the use: Let you control the organization, scope, and privacy of path
- Paths: A way of naming an item such as a struct function, or module
## Packages and Crates
- crate: smallest amount of code that Rust compiler consider at a time
- binary crate:
- programs you can compile to an executable that you can run
- library crate:
- don't have main
- don't compile to an executable
- shared with multiple pojects
- Rustaceans says `crate`, they mean library crate
- package: a bundle of one or more crates that provides a set of functionality
- contains a Cargo.toml that describe how to build one or more crates
- Cargo, is binary crate for build, also contgains a library crate
- can contain as many binary crate
- must contain at least one crate
```rust
// package
.
├── Cargo.toml
├── bin // package can have multiple binary crates
└── src
├── lib.rs // crate root, it can be library or binary
└── main.rs // crate root, it can be binary
```
## Defining Modules to Control Scope and Privacy
### Modules Cheat Sheet
- How module work
- Start from the crate root: when compiling a crate, the compiler first looks in the crate root file `src/main.rs` or `src/lib.rs`
- Declaring modules: as `mod garden` declared, the compiler will look for module's code in these places: Inline > `src/garden.rs` > `src/garden/mod.rs`
- Declaring submodules: as `mod vegetables` decalred in `src/garden.rs`, the compiler will look for the submodule's code within the directory named for the parent module in these places: Inline > `src/garden/vegetables.rs` > `src/garden/vegetables/mod.rs`
- Plath to code in modules: Once a module is part of your crate, you can refer to code. For example, `Asparagus` type in the garden vegetable module would be found at `crate::garden::vegetables::Asparagus`
- Private vs Public: Private from its parent modules by default, use `pub mod` to make a module public
- The use keyword: Within a scope, the `use` keyword creates shortcuts to item to reduce repetion of long paths. `use crate::garden::vegetables::Asparagus;` make you only need to write just `Asparagus`
- The crate’s directory, also named backyard, contains these files and directories:
```rust
backyard
├── Cargo.lock
├── Cargo.toml
└── src
├── garden
│ └── vegetables.rs
├── garden.rs
└── main.rs
```
- The crate root file in this case is src/main.rs, and it contains:
```rust
use crate::garden::vegetables::Asparagus;
pub mod garden;
fn main() {
let plant = Asparagus {};
println!("I'm growing {:?}!", plant);
}
```
- The `pub mod garden;` line tells the compiler to include the code it finds in `src/garden.rs`
```rust
pub mod vegetables;
```
- `pub mod vegetables;` means the code in `src/garden/vegetables.rs` is included too
```rust
#[derive(Debug)]
pub struct Asparagus {}
```
### Grouping Related Code in Modules
```rust
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn server_order() {}
fn take_payment() {}
}
}
```
here is `module tree`. `src/main.rs` and `src/lib.rs` are called crate roots
```
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
```
### Paths for Referring to an Item in the Module Tree
- `An absolute path` is the full path starting from a crate root; for code from an external crate, the absolute path begins with the crate name, and for code from the current crate, it starts with the literal crate.
- `A relative path` starts from the current module and uses self, super, or an identifier in the current module.
- Both absolute and relative paths are followed by one or more identifiers separated by double colons `(::)`
```rust
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist(); // error, module `hosting` is private
// Relative path
front_of_house::hosting::add_to_waitlist();
}
```
- Module are the privacy boundary in Rust
- All items (functions, methods, structs, enums, modules and constants) are private by default
- `pub` keyword to make an item public
- Items in a parent module `can’t use` the private items `inside child modules`, but items in child modules `can us`e the items in `their ancestor modules`.
```rust
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
front_of_house::hosting::add_to_waitlist();
}
```
### Starting Relative Paths with `super`
- `super`, is like starting a file system path with `...`
- in case of `super` is root
```rust
fn deliver_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
super::deliver_order();
}
fn cook_order() {}
}
```
### Making Structs and Enums Public
- `pub` before a struct definition, we make the struct public
```rust
mod back_of_house {
// pub to make public struct
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String,
}
impl Breakfast {
// If Breakfast didn’t have such a function, we couldn’t create an instance of
// Breakfast in eat_at_restaurant because we couldn’t set the value of the private
// seasonal_fruit field in eat_at_restaurant.
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
}
pub fn eat_at_restaurant() {
// Order a breakfast in the summer with Rye toast
let mut meal = back_of_house::Breakfast::summer("Rye");
// Change our mind about what bread we'd like
meal.toast = String::from("Wheat");
println!("I'd like {} toast please", meal.toast);
// The next line won't compile if we uncomment it; we're not allowed
// to see or modify the seasonal fruit that comes with the meal
// meal.seasonal_fruit = String::from("blueberries");
}
```
- if we make an enum public, all of its variants are then public
```rust
mod menu {
pub enum Appetizer {
Soup,
Salad,
}
}
fn main() {
let order1 = menu::Appetizer::Soup;
let order2 = menu::Appetizer::Salad;
}
```
## Bringing Paths into Scope with the use Keyword
- Create a shortcut to a path with the `use` keyword once, and then use the shorter name everywhere else in the scope
- Adding use and a path in a scope is similar to creating a symbolic link in the filesystem. By adding use crate::front_of_house::hosting in the crate root, hosting is now a valid name in that scope
```rust
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
```
- `use` only creates the shortcut for the particular scope in which the `use` occurs
```rust
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
mod customer {
pub fn eat_at_restaurant() {
hosting::add_to_waitlist(); // error, failed to resolve: use of undeclared crate or module `hosting`
}
}
```
### Creating Idiomatic use Paths
- Specifying the parent module when calling the function makes it clear that the function isn’t locally defined
```rust
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting::add_to_waitlist; // make it confusing between local defined function
pub fn eat_at_restaurant() {
add_to_waitlist();
}
```
- On the other hand, when bringing in structs, enums, and other items with use, it’s idiomatic to specify the full path
```rust
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, 2);
}
```
- There’s no strong reason behind this idiom: it’s just the convention that has emerged, and folks have gotten used to reading and writing Rust code this way.
- If we’re bringing two items with the same name into scope with use statements, because Rust doesn’t allow that
```rust
use std::fmt;
use std::io;
fn function1() -> fmt::Result {
// --snip--
}
fn function2() -> io::Result<()> {
// --snip--
}
```
### Providing New Names with the as Keyword
- Same name into the same scope with use: after the path, we can specify as and a new local name, or alias, for the type
```rust
use std::fmt::Result;
use std::io::Result as IoResult;
fn function1() -> Result {
// --snip--
}
fn function2() -> IoResult<()> {
// --snip--
}
```
### Re-exporting Names with pub use
- Bring a name into scope with the use keyword, the name available in the new scope is private.
- To `re-exporting`, combine `pub` and `use`
```rust
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
```
### Using External Packages
- Add package to `Cargo.toml` we want to use. for example `rand`.
- Cargo will down load `rand` package
- Any dependencies from crates.io and make rand available to our project.
```toml
rand = "0.8.5"
```
- Use line starting with the name of the crate, rand, and listed the items we wanted to bring into scope
```rust
use rand::Rng;
fn main() {
let secret_number = rand::thread_rng().gen_range(1..=100);
}
```
- Standard std library is also a crate that’s external to our package.
- Because the standard library is shipped with the Rust language, we don’t need to change Cargo.toml to include std
- But we do need to refer to it with `use` to bring items from there into our package’s scope
```rust
use std::collections::HashMap;
```
### Using Nested Paths to Clean Up Large use Lists
- Use curly brackets after two colons to bring the same items into scope in one line.
```rust
use std::cmp::Ordering;
use std::io;
// use ::{,} to bring itmes
use std::{cmp::Ordering, io};
```
- We can use a nested path at any level in a path, which is useful when combining two use statements that share a subpath
```rust
use std::io;
use std::io::Write;
// use `self` in the nested path
use std::{self, Write}
```
### The Glob Operator
- To bring all public items defined in a path into scope
```rust
use std::collections::*;
```
## Separating Modules into Different Files
- We’ll extract modules into files instead of having all the modules defined in the crate root file. see below
```
src
└── lib.rs
└── front_of_house
└── hosting.rs
```
- `src/lib.rs` decalre `mod front_of_house` at the begin of the file
```rust
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
```
- `src/front_of_house.rs` decalre `mod hositing` and makes it as public
```rust
pub mod hosting;
```
- `src/front_of_house/hosting.rs` contains the definitions
```rust
pub fn add_to_waitlist() {}
```
- alternate File Paths
- src/front_of_house.rs (what we covered)
- src/front_of_house/mod.rs (`older style`, still supported path)
# Common Collections
Rust’s standard library includes a number of very useful data structures called collections. Collections can contain multiple values. Unlike the built-in array and tuple types
- vector: allows you to store a variable number of values next to each other
- string: collection of characters
- hash map: allows you to associate a value with a particular key
## Storing Lists of Values with Vectors
### Creating a New Vector
> [samples](./5-collections/src/main.rs)
```rust
let v: Vec = Vec::new(); // added a type annotation
let v = vec![1, 2, 3]; // use vec! macro
```
### Update a Vector
```rust
let mut v = Vec::new();
v.push(5);
v.push(6);
v.push(7);
v.push(8);
```
### Reading Elements of Vectors
- Two ways to reference a value. indexing[] and `get`
- Out of index: [] will cause the panic, `get` method returns `None` without panicking
```rust
let v = vec![1, 2, 3, 4, 5];
let third: &i32 = &v[2];
println!("The third element is {third}");
let third: Option<&32> = v.get(2);
match third {
Some(third) => println!("The third element is {third}");
None => println!("There is no thiid element.");
}
```
- Cannot have mutable and immutable references in the same scope
```rust
let mut v = vec![1, 2, 3, 4];
let first = &v[0]; // immutable borrow
v.push(6); // mutable borrow, vector might require allocating new memory and copy the old elms
println!("fist is {}", first) // immutable borrow used here
```
### Iterating over the values in a Vector
> [samples](./5-collections/src/main.rs)
```rust
let v = vec![100, 32, 57];
for i in &v {
println!("{i}");
}
// with mutable references
let mut v = vec![100, 32, 57];
for i in &mut v {
*i + 50; // use * dereference operator to get to the value
}
```
### Using an enum to store multiple types
- Vector can only store values that are the sample type. This can be inconvenient. To store different use enum
- If you don’t know the exhaustive set of types a program, enum doesn't work, use Trait instead of
```rust
// this enum has multiple types
enum SpreadsheetCell {
Int(32),
Float(f64),
Text(String)
}
// holds different types
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue"))
SpreadsheetCell::Float(10.12)
]
```
### Dropping a Vector Drops Its Elements
- Vector is freed when it goes out of scope
```rust
{
let v = vec![1, 2, 3, 4];
// do stuff with v
} // <- v goes out of scope and is free here
```
## Storing UTF-8 Encoded Text With Strings
### What Is a String?
- String is only one type in the core language in Rust, `str`
- which is the string slice, usually seen in its borrowed
- String are implemented as a collection of bytes
- String, gowable, mutable, owned, UTF-8 encoded type
### Creating a New String
- Many of some operations available with Vec
- String is actually impemented as a wrapper around a vector of bytes with some extra gurantees restrictions and capbilities
```rust
// create a new empty string with the new function
let mut s = String::new();
// create a string containing `string`
let data = "initial contents"
let s = data.to_string();
// create a string from literal directly
let s = "initial contents".to_string();
// create a string from a string literal same as to_string
let s = String::from("initial content");
```
- Strings are UTF-8 encoded
```rust
let hello = String::from("السلام عليكم");
let hello = String::from("Dobrý den");
let hello = String::from("Hello");
let hello = String::from("שָׁלוֹם");
let hello = String::from("नमस्ते");
let hello = String::from("こんにちは");
let hello = String::from("안녕하세요");
let hello = String::from("你好");
let hello = String::from("Olá");
let hello = String::from("Здравствуйте");
let hello = String::from("Hola");
```
### Updading a String
> String can grow in size and its contents can change same as Vec
#### Appending to a String with push_str and push
```rust
let mut s = String::from("foo");
s.push_str("bar"); // method takes a string slice
let mut s1 = String::from("foo");
let s2 = "bar";
s1.push_str(s2);
println!("s2 is {s2}"); // s2 ownership still alive
// single character
s.push('l');
```
#### Concatenation with the + Operator or the format! Macro
```rust
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
// note s1 has been moved here and can no longer be used
let s3 = s1 + &s2; // 1. s1 will call `add(self, s:&str) -> String`
// 2. &s2 will be `deref coercion`, &s2 -> &s2[..]
// 3. `add` keep `s`'s ownership. s2 is still valid
// 4. `add` takes ownership of self, s1 will be moved into the `add`
// 5. s1 will no longer be valid after this operation
// 6. append a copy of the content of s2
// 7. return ownership of the result
// format! for multiple concatenate
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{}-{}-{}", s1, s2, s3);
```
### Indexing into Strings
- Rust doesn’t allow us to index into a String to get a character is that indexing
```rust
// operations are expected to always take constant time (O(1)).
let s1 = String::from
let h = s1[0]; // `String` error cannot be indexed by {integer}
```
#### Internal Representation
```rust
let hello = String::From("hola").len(); // 4 byte, take 1 byte for UTF-8
let hello = String::from("Здравствуйте").len(); // 24 byte, take 2 byte for UTF-8
let hello = "Здравствуйте";
let answer = &hello[0]; // return 208 not '3', which is first byte
```
#### Bytes and Scalar Values and Grapheme Clusters! Oh My!
- UTF-8 is that there are actually three relevant ways to look at strings from Rust’s perspective: as `bytes`, `scalar values`, and `grapheme clusters` (letters)
- How “नमस्ते” (Devanagari scriptis) show in Rust's perspectives
```rust
// bytes (u8)
[224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164, 224, 165, 135]
// unicode scalar values (char, but 4 and 6 are not letters)
['न', 'म', 'स', '्', 'त', 'े']
// grapheme clusters, we'd get what a person would call the four `letters`
["न", "म", "स्", "ते"]
```
- Rust priovidess different ways of interpreting the raw string data which program can choose the interpretation it needs
- Rust doesn't allow us to index into a string in constant time o(1) because Rust would have to walk through the content from the begnning to the index to determine how many valid charaters there where
### Slicing Strings
- Rust asks you to be more specific via [] with a range to creat strin slice
```rust
let hello = "Здравствуйте";
let s = &hello[0..4]; // 4byte, will be Зд
let s = &hello[0..1]; // panic, index is not a char boundary
```
### Methods for Iterating Over Strings
- Use `chars` for individual Unicode scalar values
```rust
for c in "Зд".chars() {
println!("{c}");
}
// result
З
д
for c in "Зд".bytes() {
println!("{}", c);
}
// result
208
151
208
180
```
### Strings Are Not So Simple
- To Summarize, string are complicated. Different programming languages make different choices about how to present this complexity to the programmer
- Rust has chosen to make the correct handling of String data the default behavior for all Rust programs, which means programmers have to put more thought into handling UTF-8 data upfront
## Storing Keys with Assoicated Values in Hash Maps
- `HashMap` type stores a mapping of keys of type K to values of types V using `hashing function`
- `Key` can be of any type
### Creating a New Hash Map
- Vector, Hash map store data on the heap
- `insert` to add elements
```rust
use std::collection::Hashmap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
```
### Accessing Values in a Hash Map
- `get` a value with `key`
```rust
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
let team_name = String::from("Blue");
let score = scores
.get(&team_name) // reutrn `Option(&V)`, If no value?, will return `None`
.copied() // call `copied` to get Option rather than an Option<&i32>
.unwrap_or(0); // set score to zero if scores doesn't have an entry
```
- using `for` loop
```rust
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.inseret(String::from("Blue"), 10);
scores.inseret(String::from("Yellow"), 50);
for (key, value) in &scores {
println!("{key}: {value}");
}
```
### Hash Maps and Ownership
- The values are copied into the hash map in case of types implemented the `Copy` trait, like i32
- A string value, like String, will be moved and hash map will be the owner
```rust
let field_name = String::from("Favorite color");
let field_value = String::from("Blue":);
let mut map = HashMap::new();
map.insert(field_name, filed_value); // values are copied into the hash
// field_name and field_value are invalid at this point, try using them and
// see what compiler error you get!
```
### Updating a Hash Map
> When you want to change the data in a hash map, you have to decide how to handle the case when a key already has a value assigned
#### Overwriting a Value
```rust
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Blue"), 25);
println!("{:?}", scores);
// result
{"Blue": 25}
```
#### Adding a Key and Value Only If a Key Isn’t Present
```rust
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
// `or_insert` method on Entry is defined to return a mutable reference
scores.entry(String::from("Yellow")).or_insert(50);
scores.entry(String::from("Blue")).or_insert(50);
println!("{:?}", scores);
// result
{"Yellow": 50, "Blue": 10}
```
#### Updating a Value Based on the Old Value
```rust
use std::collections::HashMap;
let text = "hello world wonderful world";
let mut map = HashMap::new();
for word in text.split_whitespace() {
let count = map.entry(word).or_insert(0);
*count += 1; // update old value
}
println!("{:?}", scores);
// result
{"world": 2, "hello": 1, "wonderful": 1}
```
### Hashing Functions
- Uses a hashing function called SipHash that can provide resistance to Denial of Service (DoS) attacks involving hash tables. This is not the fastest hashing algorithm available, but the trade-off for better security that comes with the drop in performance is worth it
- You can switch to another function by specifying a different hasher. A hasher is a type that implements the BuildHasher trait
# Errror Handling
- Rust requires you to acknowledge the possibility of an error and take some action before your code will compile.
- Rust group errors into two types of categories:
- recoverable: file not found error, Result
- unrecoverable: symptoms of bugs, accessing end of an array, panic!
- Rust doesn't have exceptions, manage Result or get panic!
## Unrecoverable Erros With panic!
- When a panic occurs(or `panic!`), the program starts `unwinding` but it cleanup is a lot of work so Rust allow you to choose the alrernative of immediately aborting
```
[profile.release]
panic = 'abort'
```
## Using a panic! Backtrace
- Unlike C, Rust will stop execution and refuse to contitue in case of invalid index to protect your program from this sort of vulnerablity like `buffer overread`
- To show backtrace, `RUST_BACKTRACE=1 cargo run`
- To enable cargo symbol `cargo build, cargo run` without `--release`
```rust
fn main() {
let v = vec![1, 2, 3];
v[99];
}
```
## Recoverable Error with Result
- Opening file failed is that no need to terminate the process, we can create the file
- Function returl `Result` because function could fail
```rust
enum Result {
Ok(T),
Err(E),
}
```
- When ssucceeds return Ok(T), failed, return Err(E)
```rust
use std:fs::File;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => panic!("There was a problem opening file: {:?}", error),
};
}
```
### Matching on Different Errors
- Take different actions for different failure reasons
```rust
use std:fs::File;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) = fc,
Err(e) = panic!("Tried to create file but there was a problem {:?}", e),
},
other_error => panic!("There was a problem opening file: {:?}", error)
},
};
}
```
### Alternatives to Using match with Result
- Using closures and the `unwrap_or_else` without `match`
```rust
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let f = File.open("hello.txt").map_err(|error| {
if (error.kind()) == ErrorKind.NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("Tried to create file but there was a problem {:?}", e)
})
} else {
panic!("There was a problem opening file: {:?}", error)
}
})
}
```
### Shortcuts for Panic on Error: unwrap and expect
- Using `match` works well but verbose
- `Result` type has many helper methods defined
- `Ok` variant in `Result` reutn the value inside the `Ok`
- `Err` variant in `Result`, `unwrap` will call `panic!`
```rust
use std::fs::File;
fn main() {
// panic! call without hello.txt
let greeting_file = File::open("hello.txt").unwrap();
}
```
- `expect` lets us choose the `panic!` error message
- Using `expect` instead of `unwrap` and providing `good error message`
```rust
use std:fs:File;
fn main() {
// unwarp, shortcut method of match, return Ok or Err
let f = File::open("hello.txt").unwrap()
// expect, let us choos the panic, return the fild handle or call panic! macro
let f = File::open("hello.txt").expect("Failed to open hello.txt)
}
```
### Propagating Errors
- Return the error to the calling code instead of handling the error within the function itself
```rust
fn read_username_from_file() -> Rsult {
let f = File::open("hello.txt");
let mut f = match f {
Ok(file) => file, // return String
Err(e) => return Err(e), // return error to the code that called the function
// use the return keyword to return early out of the
// function entirely and pass the error value from File::open
}
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e), // return error
}
}
```
#### A Shortcut for Propagating Errors: the ? Operator
- Uses the ? operator, placed after a Result value is defined to work in almost the same way as the match expressions we defined to handle the Result values
```rust
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result {
let mut username_file = File::open("hello.txt")?;
let mut username = String::new();
username_file.read_to_string(&mut username)?;
Ok(username)
}
```
- Shorten the code futher by chaining mehtod call immediately after the `?`
```rust
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result {
let mut s = String::new();
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
}
```
- More shorten the code by using `fs::read_to_string`
```rust
fn read_username_from_file() -> Result {
fs::read_to_string("hello.txt")
}
```
#### Where The ? Operator Can Be Used
- `?` operator can only be used in functions whose return type is compatible with the value the `?` is used on
```rust
fn main() { // main is incompatible with `Result` or `Option`
let f = File::open("hello.txt"); // ^ cannot use the `?` operator in a function that returns `()`
}
```
- `?` can be used with `Option` values
- The behavior of the ? operator when called on an Option is similar to its behavior when called on a Result
- If the value is None, the None will be returned early from the function at that point
- If the value is Some, the value inside the Some is the resulting value of the expression and the function continues
```rust
fn last_char_of_first_line(text: &str) -> Option {
text.lines().next()?.chars().last()
}
```
- Because it’s the entry and exit point of executable programs, and `there are restrictions` on what its return type can be for the programs to behave as expected.
- Main can also return a Result<(), E>
- You can read Box to mean “any kind of error.”
- Main function returns a Result<(), E>, the executable will exit with a value of 0 if main returns Ok(())
- Main function will exit with a nonzero value if main returns an Err value
```rust
use std::error::Error;
use std::fs::File;
fn main() -> Result<(), Box> {
let greeting_file = File::open("hello.txt")?;
Ok(())
}
```
## To panic! or Not ro panic!
- You could call panic! for any error situation, whether there’s a possible way to recover or not, but then you’re making the decision that a situation is unrecoverable on behalf of the calling code. When you choose to return a Result value, you give the calling code options
### Examples, Prototype Code, and Tests
When you’re writing an example to illustrate some concept, also including robust error-handling code can make the example less clear. In examples, it’s understood that a call to a method like unwrap that could panic is meant as a placeholder for the way you’d want your application to handle errors, which can differ based on what the rest of your code is doing.
### Cases in Which You Have More Information Than the Compiler
if you can ensure by manually inspecting the code that you’ll never have an Err variant, it’s perfectly acceptable to call unwrap, and even better to document the reason you think you’ll never have an Err variant in the expect text.
```rust
use std::net::IpAddr;
let home: IpAddr = "127.0.0.1"
.parse()
.expect("Hardcoded IP address should be valid");
```
### Guidelines for Error Handling
- Panic when it’s possible that your code could end up in a `bad state`
- The `bad state` is something that is unexpected, as opposed to something that will likely happen occasionally, like a user entering data in the wrong forma
- Your code after this point needs to rely on not being in this bad state, rather than checking for the problem at every step.
- There’s not a good way to encode this information in the types you use
> If someone calls your code and passes in values that don’t make sense, it’s best to return an error if you can so the user of the library can decide what they want to do in that case. However, in cases where continuing could be insecure or harmful, the best choice might be to call panic! and alert the person using your library to the bug in their code so they can fix it during development. However, when failure is expected, it’s more appropriate to return a Result than to make a panic! call
### Creating Custom Types for Validation
- Parse the guess as an i32 instread of only a u32 to allow potentially negative numbers, and then add a check for the number being in range
- However, this is not an ideal solution: if it was absolutely critical that the program only operated on values between 1 and 100, and it had many functions with this requirement, having a check like this in every function would be tedious
```rust
loop {
// --snip--
let guess: i32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
if guess < 1 || guess > 100 {
println!("The secret number will be between 1 and 100.");
continue;
}
match guess.cmp(&secret_number) {
// --snip--
}
- If value doesn’t pass this test, we make a panic! call, which will alert the programmer who is writing the calling code that they have a bug they need to fix, because creating a Guess with a value outside this range would violate the contract that Guess::new is relying on
- Function that has a parameter or returns only numbers between 1 and 100 could then declare in its signature that it takes or returns a Guess rather than an i32 and wouldn’t need to do any additional checks in its body.
pub struct Guess {
value: i32,
}
impl Guess {
pub fn new(value: i32) -> Guess {
if value < 1 || value > 100 {
panic!("Guess value must be between 1 and 100, got {}.", value);
}
Guess {
value
}
}
pub fn value(&self) -> i32 {
self.value
}
}
```
# Generic Types, Traits, and Lifetimes
- `generic` is tool for effectively handling the duplication of concepts in Rust
- Functions can take parameter of some generic type
- `traits` define behavior in a generic way which can combine with generic types to constrain a generic type to accept only those types that a particular behavior as opposed to just any type
## Removing Duplication by Extracting a Function
- We've now been tasked with finding the largest number in two different lists of numbers
```rust
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let mut largest = &number_list[0];
for number in &number_list {
if number > largest {
largest = number;
}
}
println!("The largest number is {}", largest);
let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];
let mut largest = &number_list[0];
for number in &number_list {
if number > largest {
largest = number;
}
}
println!("The largest number is {}", largest);
}
```
- Identify duplicate code.
- Extract the duplicate code into the body of the function and specify the inputs and return values of that code in the function signature.
- Update the two instances of duplicated code to call the function instead.
```rust
fn largest(list: &[i32]) -> &i32 {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);
println!("The largest number is {}", result);
let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];
let result = largest(&number_list);
println!("The largest number is {}", result);
}
```
## Generic Data Types
### In Function Definitions
- Place the generics in the signature of the function where we would usually specify the data types of th