Rust_Learning

记录我的 Rust 学习

Cargo 使用

Copy
cargo new
cargo build
cargo run
cargo check
cargo doc --open #build documentation provided by all your dependencies locally and open it in your browser

参考链接

Rust 语言圣经

The Rust Programming Language

Rust 语言实战

PNGme

Rust 特点

Patterns and the match construct

a strong, static type system.

type inference

shadow

Rust uses the term panicking when a program exits with an error

requiring type annotations in function definitions

Rust's goal is to compile programs into efficient binaries that require as few runtime checks as possible

A foundational goal of Rust is to ensure that your programs never have undefined behavior

Chapter3 Common Programming Concepts

constant v.s. variable

Constants aren’t just immutable by default;they’re always immutable.

Constants can be declared in any scope

Constants may be set only to a constant expression, not the result of a value that could only be computed at runtime.

shadow ends until

it itself is shadowed

the scope ends

Integer division truncates toward zero to the nearest integer

tuple 和 array 区别

tuple 的各类型可以不同；

array 的大小固定，在定义时就指定了

tuple 更灵活，可存储不同类型，一般用于临时组合数据

array 大小固定，用于存储大量相同类型的数据

expression and statement

Expressions do not include ending semicolons

statement does not return a value

the return value of the function is synonymous with the value of the final expression in the block of the body of a function

Control Flow

This means the values that have the potential to be results from each arm of the if must be the same type

Rust does not have a concept of "truthy" or "falsy" values.So The condition to an if-expression must be a boolean

Chapter4 Understanding Ownship

The stack holds data associated with a specific function, while the heap holds data that can outlive a function

Rust does not allow programs to manually deallocate memory. That policy avoids the kinds of undefined behaviors shown above.

Box deallocation principle : If a variable owns a box, when Rust deallocates the variable's frame, then Rust deallocates the box's heap memory.

Moved heap data principle: if a variable x moves ownership of heap data to another variable y, then x cannot be used after the move.

A reference is a kind of pointer.

Rust implicitly inserts dereferences and references in certain cases, such as calling a method with the dot operator

Pointer Safety Principle: data should never be aliased and mutated at the same time.

Permissions are defined on paths and not just variables. A path is anything you can put on the left-hand side of an assignment.

Creating a reference to data ("borrowing" it) causes that data to be temporarily read-only until the reference is no longer used.

Rust's borrow checker does not contain different paths for a[0], a[1], and so on. It uses a single path a[_] that represents all indexes of a.

Slices are special kinds of references because they are "fat" pointers, or pointers with metadata. Here, the metadata is the length of the slice.

总结

优势

improving runtime performance by avoiding garbage collection

improving predictability by preventing accidental "leaks" of data.

Pointers can be created through

boxes (pointers owning data on the heap)

references (non-owning pointers).

move v.s. borrow

A move of a variable with a non-copyable type (like Box or String) requires the RO permissions, and the move eliminates all permissions on the variable. That rule prevents the use of moved variables:

Borrowing a variable (creating a reference to it) temporarily removes some of the variable's permissions

An immutable borrow creates an immutable reference, and also disables the borrowed data from being mutated or moved.

A mutable borrow creates a mutable reference, which* disables the borrowed data from being read, written, or moved*

use-after-free: Immutable borrows remove the W permission to avoid use-after-free,

double-frees: Dereferences of references to non-copyable data do not have the O permission to avoid double-frees

Chapter5 Struct

Rust does not have a keyword for constructor functions. The idiomatic way to define a constructor function is to make an associated function called new, but that is not enforced by the language.

tuple struct. e.g. struct Color (i32,i32,i32);

Rust will insert as many references and dereferences as needed to make the types match up for the self parameter

Rust does not auto-derive Copy for stability across API changes. #[derive(Copy, Clone)]

when you see an error like "cannot move out of *self", that's usually because you're trying to call a self method on a reference like &self or &mut self. Rust is protecting you from a double-free

Chapter6 Enum

advantage to using an enum rather than a struct:

each variant can have different types and amounts of associated data

the name of each enum variant that we define also becomes a function that constructs an instance of the enum

you can put any kind of data inside an enum variant: strings, numeric types, or structs, for example. You can even include another enum

The Option Enum

the compiler can check whether you’ve handled all the cases you should be handling

a null is a value that is currently invalid or absent for some reason.

Rust does not have nulls, but it does have an enum that can encode the concept of a value being present or absent.

The function Option::unwrap expects self, meaning it expects ownership of arg. However arg is an immutable reference to an option, so it cannot provide ownership of the option.

match

Each match is tried from top to bottom

opt is a plain enum — its type is Option and not a reference like &Option. Therefore a match on opt will move non-ignored fields like s.

If we want to peek into opt without moving its contents, the idiomatic solution is to match on a reference:

if let

if let as syntax sugar for a match that runs code when the value matches one pattern and then ignores all other values.

The block of code that goes with the else is the same as the block of code that would go with the _ case in the match expression that is equivalent to the if let and else

Chapter7 Managing Growing Projects with Packages, Crates, and Modules

Packages: A Cargo feature that lets you build, test, and share crates

a bundle of one or more crates that provides a set of functionality.

A package can contain as many binary crates as you like, but at most only one library crate.

Using External Packages

the standard std library is also a crate that’s external to our package.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

Crates: A tree of modules that produces a library or executable

Binary create: must have a function called main

Library crates : define functionality intended to be shared with multiple projects.
Rustaceans say “crate”, they mean library crate, and they use “crate” interchangeably with the general programming concept of a “library".

Modules and use: Let you control the organization, scope, and privacy of paths

用处

let us organize code within a crate for readability and easy reuse

allow us to control the privacy of items, because code within a module is private by default

parent and child

all items (functions, methods, structs, enums, modules, and constants) are private to parent modules by default.

Items in a parent module can’t use the private items inside child modules, but items in child modules can use the items in their ancestor modules.

Paths: A way of naming an item, such as a struct, function, or module

the idiomatic way

Bringing the function’s parent module into scope with use

when bringing in structs, enums, and other items with use, it’s idiomatic to specify the full path

use std::io::Result as IoResult;

we can use nested paths to bring the same items into scope in one line.use std::{cmp::Ordering, io};,use std::io::{self, Write};

Chapter8

Vector

Reason for a reference to the first element care about changes at the end of the vector

adding a new element onto the end of the vector might require allocating new memory and copying the old elements to the new space

the reference to the first element would be pointing to deallocated memory

Vec::push moves its argument, so s is not usable after calling v.push(s)

When the vector gets dropped, all of its contents are also dropped, meaning the integers it holds will be cleaned up.

String

the compiler can coerce the &String argument into a &str

Rust strings don’t support indexing to avoid returning an unexpected value and causing bugs that might not be discovered immediately

three relevant ways to look at strings from Rust’s perspective

bytes

scalar values

grapheme clusters

The best way to operate on pieces of strings is to be explicit about whether you want characters or bytes

&str is a promise that the byte sequence it points to will always be valid UTF-8

hash map

Hash maps are useful when you want to look up data not by using an index, as you can with vectors, but by using a key that can be of any type

For types that implement the Copy trait, like i32, the values are copied into the hash map. - For owned values like String, the values will be moved and the hash map will be the owner of those values

chapter10

Generic Data Types

Rust instead requires you to state the expected capabilities of generic types up front

Without restrictions, a generic type T has no capabilities: it cannot be printed, cloned, or mutated (although it can be dropped).

Rust does not have inheritance-like mechanisms for specializing methods as you might find in an object-oriented language,

Monomorphization is the process of turning generic code into specific code by filling in the concrete types that are used when compiled

const 泛型： const N : usize

trait

A trait defines functionality a particular type has and can share with other types.

One restriction to note is that we can implement a trait on a type only if at least one of the trait or the type is local to our crate.

Default implementations can call other methods in the same trait, even if those other methods don’t have a default implementation

traits in parameter
Copy
fn some_function<T:Display + Clone ,U: Clone + Debug>(t:&T,u:&U) -> i32{}
// Clearer Trait Bounds with where Clauses
fn some_function<T,U>()(t:&T,u:&U) -> i32
where
T:Display + Clone ,
U: Clone + Debug
{}

can only use impl Trait if you’re returning a single type

Using Trait Bounds to Conditionally Implement Methods
Copy
use std::fmt::Display;

struct Pair<T> {
x: T,
y: T,
}

impl<T> Pair<T> {
fn new(x: T, y: T) -> Self {
Self { x, y }
}
}

impl<T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.x >= self.y {
println!("The largest member is x = {}", self.x);
} else {
println!("The largest member is y = {}", self.y);
}
}
}

conditionally implement a trait for any type that implements another trait
Copy
impl<T: Display> ToString for T {
// --snip--
}

lifetime

Lifetime annotations don’t change how long any of the references live. Rather, they describe the relationships of the lifetimes of multiple references to each other without affecting the lifetimes

the names of lifetime parameters must start with an apostrophe (') and are usually all lowercase and very short

when we specify the lifetime parameters in this function signature, we’re not changing the lifetimes of any values passed in or returned. Rather, we’re specifying that the borrow checker should reject any values that don’t adhere to these constraints.

in early versions (pre-1.0) of Rust,every reference needed an explicit lifetime

The compiler uses three rules to figure out the lifetimes of the references when there aren’t explicit annotations.

the compiler assigns a different lifetime parameter to each lifetime in each input type.

if there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters

if there are multiple input lifetime parameters, but one of them is &self or &mut self because this is a method, the lifetime of self is assigned to all output lifetime parameters.

The Static Lifetime 'static

The string literals is stored directly in the program’s binary, which is always available. Therefore, the lifetime of all string literals is 'static.

'static means "live for the entire program", and so data under a static reference must never be deallocated.

生命周期标注并不会改变任何引用的实际作用域

chapter11

The output from running the automatically generated test

The 0 measured statistic is for benchmark tests that measure performance.

Doc-tests is for the results of any documentation tests

常用命令

Copy
assert!();
assert_eq!();
assert_ne! // will pass if the two values we give it are not equal and fail if they’re equal.

Running Tests in Parallel or Consecutively

When you run multiple tests, by default they run in parallel using threads

cargo test -- --test-threads=1

cargo test -- --show-output

cargo test --help

cargo test -- --help

Copy
#[test]
#[ignore]

cargo test -- --ignored
cargo test -- --include-ignored

We can specify part of a test name, and any test whose name matches that value will be run.

test-organization

Unit tests exercise different parts of a library separately and can test private implementation details.

Unit tests exercise different parts of a library separately and can test private implementation details.

chapter12

in cases where the desired function is nested in more than one module, we’ve chosen to bring the parent module into scope rather than the function.

TDD(Test Driven Develop)

Write a test that fails and run it to make sure it fails for the reason you expect.

Write or modify just enough code to make the new test pass.

Refactor the code you just added or changed and make sure the tests continue to pass.

Repeat from step 1!

chapter13

Rust will infer the types of arguments/returns for closures, but not top-level functions

Closures can capture values from their environment in three ways

borrowing immutably

borrowing mutably

taking ownership.

Fn traits

FnOnce

FnMut

Fn

iterator adaptors are lazy, and we need to consume the iterator here.