七万号·数据平台实战手记

Back

Fundamental-Types#

There are many, many types of books in the world, which makes good sense, because there are many, many types of people, and everybody wants to read something different. —Lemony Snicke image.png > image.png > image.png

Fixed-Width Numeric Types#

image.png image.png

// 10_u8.checked_mul(10).expect("数值溢出");
// 100_u8.checked_add(200).expect("");
// let x = 100_u8;
// let y = 100_u8;
// // Do the addition; panic if it overflows.
// let sum = x.checked_add(y).unwrap();
// 超出不报错
500_u16.wrapping_mul(500);  // 53392
500_i16.wrapping_mul(500); // -12144
// In bitwise shift operations, the shift distance
// is wrapped to fall within the size of the value.
// So a shift of 17 bits in a 16-bit type is a shift
// of 1.
// assert_eq!(5_i16.wrapping_shl(17), 10);
rust

image.png image.png

Characters#

assert_eq!('*' as i32, 42);
assert_eq!('ಠ' as u16, 0xca0);
assert_eq!('ಠ' as i8, -0x60); // U+0CA0 truncated to eight bits, signed
rust
assert_eq!('*'.is_alphabetic(), false);
assert_eq!('β'.is_alphabetic(), true);
assert_eq!('8'.to_digit(10), Some(8));
assert_eq!('ಠ'.len_utf8(), 3);
assert_eq!(std::char::from_digit(2, 10), Some('2'));
rust

Tuples#

let text = "I see the eigenvalue in thine eye";
let (head, tail) = text.split_at(21);
assert_eq!(head, "I see the eigenvalue ");
assert_eq!(tail, "in thine eye");
rust
let text = "I see the eigenvalue in thine eye";
let temp = text.split_at(21);
let head = temp.0;
let tail = temp.1;
assert_eq!(head, "I see the eigenvalue ");
assert_eq!(tail, "in thine eye");
rust

Slices#

let v: Vec<f64> = vec![0.0, 0.707, 1.0, 0.707];
let a: [f64; 4] = [0.0, -0.707, -1.0, -0.707];
let sv: &[f64] = &v;
let sa: &[f64] = &a;
rust

image.png

String Literals#

println!("In the room the women come and go,
Singing of Mount Abora");
println!("It was a bright, cold day in April, and \
there were four of us—\
more or less.");
let default_win_install_path = r"C:\Program Files\Gorillas";
println!(r###"
This raw string started with 'r###"'.
Therefore it does not end until we reach a quote mark ('"')
followed immediately by three pound signs ('###'):
"###);
rust
In the room the women come and go,
Singing of Mount Abora
It was a bright, cold day in April, and there were four of us—more or less.

This raw string started with 'r###"'.
Therefore it does not end until we reach a quote mark ('"')
followed immediately by three pound signs ('###'):
plaintext

Byte Strings#

let method = b"GET";
assert_eq!(method, &[b'G', b'E', b'T']);
rust

Strings#

let noodles = "noodles".to_string();
let oodles = &noodles[1..];
let poodles = "ಠ_ಠ";
rust
let oodles = &noodles[1..];
let oodles = &noodles[1..];
    ^^^^^^
The variable `oodles` contains a reference with a non-static lifetime so
can't be persisted. You can prevent this error by making sure that the
variable goes out of scope - i.e. wrapping the code in {}.
plaintext

image.png

assert_eq!("ಠ_ಠ".len(), 7);
assert_eq!("ಠ_ಠ".chars().count(), 3)
rust
()
plaintext

image.png

let error_message = "too many pets".to_string();
rust
println!("{}",error_message)
rust
too many pets
()
plaintext

format string#

println!("{}",format!("{}°{:02}′{:02}″N", 24, 5, 23))
rust
24°05′23″N
()
plaintext
let bits = vec!["veni", "vidi", "vici"];
rust
bits.concat()
rust
"venividivici"
plaintext
bits.join(",")
rust
"veni,vidi,vici"
plaintext
"ONE".to_lowercase()
rust
"one"
plaintext
"peanut".contains("nut")
rust
true
plaintext
"ಠ_ಠ".replace("ಠ", "■")
rust
"■_■"
plaintext
rust

Ownership-and-Moves#

Ownership#

{
let point = Box::new((0.625, 0.5)); // point allocated here
let label = format!("{:?}", point); // label allocated here
assert_eq!(label, "(0.625, 0.5)");
}
rust
()
plaintext

image.png

Abc, born 123
DEF, born 444
ASD, born 555
()
plaintext

image.png

Moves#

let s = vec!["udon".to_string(), "ramen".to_string(), "soba".to_string()];
let t = s;
let u = s;
rust
[E0382] Error: use of moved value: `s`
   ╭─[command_4:1:1]

 1 │ let s = vec!["udon".to_string(), "ramen".to_string(), "soba".to_string()];
   │     ┬
   │     ╰── move occurs because `s` has type `Vec<String>`, which does not implement the `Copy` trait
 2 │ let t = s;
   │         ┬│
   │         ╰── value moved here
   │          │
   │          ╰─ help: consider cloning the value if the performance cost is acceptable: `.clone()`
 3 │ let u = s;
   │         ┬
   │         ╰── value used here after move
───╯
plaintext

image.png

let s = vec!["udon".to_string(), "ramen".to_string(), "soba".to_string()];
let t = s.clone();
let u = s.clone();
rust

More Operations That Move#

let mut s = "Govinda".to_string();
s = "Siddhartha".to_string(); // value "Govinda" dropped here
rust
let mut s = "Govinda".to_string();
let t = s;
s = "Siddhartha".to_string(); // nothing is dropped here
rust

Moves and Control Flow#

image.png

Moves and Indexed Content#

// Build a vector of the strings "101", "102", ... "105"
let mut v = Vec::new();
for i in 101 .. 106 {
v.push(i.to_string());
}
rust
()
plaintext
// Pull out random elements from the vector.
let third = v[2]; // error: Cannot move out of index of Vec
let fifth = v[4]; // here too
rust
// 1. Pop a value off the end of the vector:
let fifth = v.pop().expect("vector empty!");
assert_eq!(fifth, "105");
rust
// 2. Move a value out of a given index in the vector,
// and move the last element into its spot:
let second = v.swap_remove(1);
assert_eq!(second, "102");
// 3. Swap in another value for the one we're taking out:
let third = std::mem::replace(&mut v[2], "substitute".to_string());
assert_eq!(third, "103");
// Let's see what's left of our vector.
assert_eq!(v, vec!["101", "104", "substitute"]);
rust
struct Person { name: Option<String>, birth: i32 }
let mut composers = Vec::new();
composers.push(Person { name: Some("Palestrina".to_string()),
birth: 1525 });
rust
let first_name = composers[0].name;
rust
[E0507] Error: cannot move out of index of `Vec<Person>`
   ╭─[command_13:1:1]

 1 │ let first_name = composers[0].name;
   │                  ┬───────┬────────
   │                  ╰────────────────── help: consider borrowing here: `&`
   │                          │
   │                          ╰────────── move occurs because value has type `Option<String>`, which does not implement the `Copy` trait
───╯
plaintext
let first_name = std::mem::replace(&mut composers[0].name, None);
assert_eq!(first_name, Some("Palestrina".to_string()));
assert_eq!(composers[0].name, None);
rust
let first_name = composers[0].name.take();
rust

Copy Types: The Exception to Moves#

let string1 = "somnambulance".to_string();
let string2 = string1;
let num1: i32 = 36;
let num2 = num1;
rust

image.png

struct Label { number: u32 }
fn print(l: Label) { println!("STAMP: {}", l.number); }
let l = Label { number: 3 };
print(l);
println!("My label number is: {}", l.number);
rust
[E0382] Error: borrow of moved value: `l`
   ╭─[command_17:1:1]

 3 │ let l = Label { number: 3 };
   │     ┬
   │     ╰── move occurs because `l` has type `Label`, which does not implement the `Copy` trait
 4 │ print(l);
   │       ┬
   │       ╰── value moved here
 5 │ println!("My label number is: {}", l.number);
   │                                    ────┬───
   │                                        ╰───── value borrowed here after move
───╯
plaintext
#[derive(Copy, Clone)]
struct Label { number: u32 }
rust
#[derive(Copy, Clone)]
struct StringLabel { name: String }
rust
[E0204] Error: the trait `Copy` cannot be implemented for this type
   ╭─[command_19:1:1]

 1 │ #[derive(Copy, Clone)]
   │          ──┬─
   │            ╰─── error: the trait `Copy` cannot be implemented for this type
 2 │ struct StringLabel { name: String }
   │                      ──────┬─────
   │                            ╰─────── this field does not implement `Copy`
───╯
plaintext

Rc and Arc: Shared Ownership#

use std::rc::Rc;
// Rust can infer all these types; written out for clarity
let s: Rc<String> = Rc::new("shirataki".to_string());
let t: Rc<String> = s.clone();
let u: Rc<String> = s.clone();
rust

image.png

assert!(s.contains("shira"));
assert_eq!(t.find("taki"), Some(5));
println!("{} are quite chewy, almost bouncy, but lack flavor", u);
rust
shirataki are quite chewy, almost bouncy, but lack flavor
plaintext
s.push_str(" noodles");
rust
[E0596] Error: cannot borrow data in an `Rc` as mutable
   ╭─[command_22:1:1]

 1 │ s.push_str(" noodles");
   │ ┬
   │ ╰── cannot borrow as mutable

   │ Note: You can change an existing variable to mutable like: `let mut x = x;`
───╯
plaintext

image.png

rust

References#

Libraries cannot provide new inabilities. —Mark Miller

use std::collections::HashMap;
type Table = HashMap<String, Vec<String>>;
rust
fn show(table:Table) {
    for (artist,works) in table {
        println!("works by {}",artist);
        for work in works {
            println!("  {}",work);
        }
    }
}
rust
let mut table = Table::new();
table.insert("Gesualdo".to_string(),
vec!["many madrigals".to_string(),
"Tenebrae Responsoria".to_string()]);
table.insert("Caravaggio".to_string(),
vec!["The Musicians".to_string(),
"The Calling of St. Matthew".to_string()]);
table.insert("Cellini".to_string(),
vec!["Perseus with the head of Medusa".to_string(),
"a salt cellar".to_string()]);
show(table);
rust
works by Gesualdo
  many madrigals
  Tenebrae Responsoria
works by Caravaggio
  The Musicians
  The Calling of St. Matthew
works by Cellini
  Perseus with the head of Medusa
  a salt cellar
plaintext
fn sort_works(table: &mut Table) {
for (_artist, works) in table {
works.sort();
}
}
rust
let mut table = Table::new();
table.insert("Gesualdo".to_string(),
vec!["many madrigals".to_string(),
"Tenebrae Responsoria".to_string()]);
table.insert("Caravaggio".to_string(),
vec!["The Musicians".to_string(),
"The Calling of St. Matthew".to_string()]);
table.insert("Cellini".to_string(),
vec!["Perseus with the head of Medusa".to_string(),
"a salt cellar".to_string()]);
sort_works(&mut table);
rust

Assigning References#

let x = 10;
let y = 20;
let mut r = &x;
if true { r = &y; }
assert!(*r == 10 || *r == 20);
rust
Error: The variable `r` contains a reference with a non-static lifetime so
can't be persisted. You can prevent this error by making sure that the
variable goes out of scope - i.e. wrapping the code in {}.
Error: The variable `r` contains a reference with a non-static lifetime so
can't be persisted. You can prevent this error by making sure that the
variable goes out of scope - i.e. wrapping the code in {}.
plaintext

image.png

References to References#

{
    struct Point { x: i32, y: i32 }
    let point = Point { x: 1000, y: 729 };
    let r: &Point = &point;
    let rr: &&Point = &r;
    let rrr: &&&Point = &rr;
    assert_eq!(rrr.y, 729);
}
rust
()
plaintext

image.png

Comparing References#

{
    let x = 10;
let y = 10;
let rx = &x;
let ry = &y;
let rrx = &rx;
let rry = &ry;
assert!(rrx <= rry);
assert!(rrx == rry);
    assert!(rx == ry); // their referents are equal
assert!(!std::ptr::eq(rx, ry)); // but occupy different addresses
    assert!(rx == rrx); // error: type mismatch: `&i32` vs `&&i32`
assert!(rx == *rrx); // this is okay
}
rust
[E0277] Error: can't compare `{integer}` with `&{integer}`
    ╭─[command_19:1:1]

 13 │     assert!(rx == rrx); // error: type mismatch: `&i32` vs `&&i32`
    │                ─┬
    │                 ╰── no implementation for `{integer} == &{integer}`
────╯
plaintext

Borrowing a Local Variable#

{
    let r;
    {
        let x = 1;
        r = &x;
    }
    assert_eq!(*r,1);
}
rust

image.png image.png image.png

Receiving References as Function Arguments#

// This code has several problems, and doesn't compile.
static mut STASH: &i32;
fn f(p: &i32) { STASH = p; }
rust
Error: free static item without body
   ╭─[command_21:1:1]

 2 │ static mut STASH: &i32;
   │ ───────────┬──────────┬
   │            ╰───────────── error: free static item without body
   │                       │
   │                       ╰── help: provide a definition for the static: ` = <expr>;`
───╯
plaintext
static mut STASH: &i32 = &128;
fn f(p: &i32) { // still not good enough
unsafe {
STASH = p;
}
}
rust
Error: lifetime may not live long enough
   ╭─[command_22:1:1]

 2 │ fn f(p: &i32) { // still not good enough
   │         ┬
   │         ╰── let's call the lifetime of this reference `'1`

 4 │ STASH = p;
   │ ────┬────
   │     ╰────── assignment requires that `'1` must outlive `'static`
───╯
plaintext
static mut STASH: &i32 = &10;
fn f(p: &'static i32) {
unsafe {
STASH = p;
    println!("{}",STASH);
}
}
rust
static WORTH_POINTING_AT: i32 = 1000;
f(&WORTH_POINTING_AT);
rust
1000
plaintext
{
    unsafe {
        println!("{}",STASH);
        println!("{}",WORTH_POINTING_AT);
    }
}
rust
10
1000
()
plaintext

Passing References to Functions#

// This could be written more briefly: fn g(p: &i32),
// but let's write out the lifetimes for now.
fn g<'a>(p: &'a i32) {  }
let x = 10;
g(&x);
rust
fn f(p: &'static i32) {  }
let x = 10;
f(&x);
// This fails to compile: the reference &x must not outlive x, but by passing it to f, we
// constrain it to live at least as long as 'static. There’s no way to satisfy everyone here,
// so Rust rejects the code.
rust

Returning References#

fn smallest(v: &[i32]) -> &i32 {
let mut s = &v[0];
for r in &v[1..] {
if *r < *s { s = r; }
}
s
}
rust
// fn smallest<'a>(v: &'a [i32]) -> &'a i32 {  }
rust
let s;
{
let parabola = [9, 4, 1, 0, 1, 4, 9];
s = smallest(&parabola);
}
assert_eq!(*s, 0);
rust
{
let parabola = [9, 4, 1, 0, 1, 4, 9];
let s = smallest(&parabola);
assert_eq!(*s, 0); // fine: parabola still alive
}
rust
()
plaintext

Structs Containing References#

// This does not compile.
struct S {
r: &i32
}
let s;
{
let x = 10;
s = S { r: &x };
}
assert_eq!(*s.r, 10); // bad: reads from dropped `x`
rust
[E0106] Error: missing lifetime specifier
   ╭─[command_44:1:1]

 3 │ r: &i32
   │    ┬
   │    ╰── expected named lifetime parameter
───╯
plaintext
struct S<'a> {
r: &'a i32
}
{
    let s;
{
let x = 10;
s = S { r: &x };
}
assert_eq!(*s.r, 10);
}
rust

Distinct Lifetime Parameters#

struct S<'a> {
x: &'a i32,
y: &'a i32
}
rust
{
    let x = 10;
let r;
{
let y = 20;
{
let s = S { x: &x, y: &y };
r = s.x;
}
}
println!("{}", r);
}
rust
struct S<'a, 'b> {
x: &'a i32,
y: &'b i32
}
rust
{
    let x = 10;
let r;
{
let y = 20;
{
let s = S { x: &x, y: &y };
r = s.x;
}
}
println!("{}", r);
}
rust
10
()
plaintext
fn f<'a>(r: &'a i32, s: &'a i32) -> &'a i32 { r } // perhaps too tight
rust
fn f<'a, 'b>(r: &'a i32, s: &'b i32) -> &'a i32 { r } // looser
rust
fn f<b>(r: &'a i32, s: &'b i32) -> &'a i32 { r }
rust

Omitting Lifetime Parameters#

struct S<'a, 'b> {
x: &'a i32,
y: &'b i32
}
rust
fn sum_r_xy(r: &i32, s: S) -> i32 {
r + s.x + s.y
}
rust
// fn sum_r_xy<'a, 'b, 'c>(r: &'a i32, s: S<'b, 'c>) -> i32
rust
fn first_third(point: &[i32; 3]) -> (&i32, &i32) {
(&point[0], &point[2])
}
rust
// fn first_third<'a>(point: &'a [i32; 3]) -> (&'a i32, &'a i32)
rust
struct StringTable {
elements: Vec<String>,
}
impl StringTable {
    fn find_by_prefix(&self,prefix:&str) -> Option<&String> {
        for i in 0..self.elements.len() {
            if self.elements[i].starts_with(prefix) {
                return Some(&self.elements[i]);
            }
        }
        None
    }
}
// fn find_by_prefix<'a, 'b>(&'a self, prefix: &'b str) -> Option<&'a String>
rust

Sharing Versus Mutation#

let v = vec![4, 8, 19, 27, 34, 10];
let r = &v;
let aside = v; // move vector to aside
r[0]; // bad: uses `v`, which is now uninitialized
rust
[unused_variables] Error: unused variable: `s`
[unused_variables] Error: unused variable: `p`
Error: The variable `r` contains a reference with a non-static lifetime so
can't be persisted. You can prevent this error by making sure that the
variable goes out of scope - i.e. wrapping the code in {}.
plaintext

image.png

let v = vec![4, 8, 19, 27, 34, 10];
{
let r = &v;
r[0]; // ok: vector is still there
}
let aside = v;
rust
fn extend(vec: &mut Vec<f64>, slice: &[f64]) {
for elt in slice {
vec.push(*elt);
}
}
rust
let mut wave = Vec::new();
let head = vec![0.0, 1.0];
let tail = [0.0, -1.0];
extend(&mut wave, &head); // extend wave with another vector
extend(&mut wave, &tail); // extend wave with an array
assert_eq!(wave, vec![0.0, 1.0, 0.0, -1.0]);
rust
extend(&mut wave, &wave);
assert_eq!(wave, vec![0.0, 1.0, 0.0, -1.0,
0.0, 1.0, 0.0, -1.0])
rust
[unused_variables] Error: unused variable: `s`
[unused_variables] Error: unused variable: `p`
[E0502] Error: cannot borrow `wave` as immutable because it is also borrowed as mutable
   ╭─[command_83:1:1]

 1 │ extend(&mut wave, &wave);
   │ ───┬── ────┬────  ──┬──
   │    ╰───────────────────── mutable borrow later used by call
   │            │        │
   │            ╰───────────── mutable borrow occurs here
   │                     │
   │                     ╰──── immutable borrow occurs here
───╯
plaintext

image.png image.png

Taking Arms Against a Sea of Objects#

image.png

rust

struct-enums-patterns#

Long ago, when shepherds wanted to see if two herds of sheep were isomorphic, they would look for an explicit isomorphism. —John C. Baez and James Dolan, “

Named-Field Structs#

// A rectangle of eight-bit grayscale pixels.
struct GrayscaleMap {
pixels: Vec<u8>,
size: (usize, usize)
}
rust
let width = 1024;
let height = 576;
let image = GrayscaleMap {
pixels: vec![0; width * height],
size: (width, height)
};
rust
image
rust
[E0277] Error: `GrayscaleMap` doesn't implement `Debug`
plaintext
/// A rectangle of eight-bit grayscale pixels.
// 模块外访问
pub struct GrayscaleMap {
pub pixels: Vec<u8>,
pub size: (usize, usize)
}
rust

Tuple-Like Structs#

#[derive(Copy, Clone, Debug)]
struct Bounds(usize, usize);
rust
let image_bounds = Bounds(1024, 768);
image_bounds
rust
The type of the variable image was redefined, so was lost.
Bounds(1024, 768)
plaintext

Unit-Like Structs#

struct A;
let a=A;
rust

Struct Layout 内存布局#

image.png

Defining Methods with impl#

/// A first-in, first-out queue of characters.
pub struct Queue {
older: Vec<char>, // older elements, eldest last.
younger: Vec<char> // younger elements, youngest last.
}
rust
let mut q = Queue { older: Vec::new(), younger: Vec::new() };
q.push('0');
q.push('1');
assert_eq!(q.pop(), Some('0'));
q.push('∞');
assert_eq!(q.pop(), Some('1'));
assert_eq!(q.pop(), Some('∞'));
assert_eq!(q.pop(), None);
rust
impl Queue {
pub fn is_empty(&self) -> bool {
self.older.is_empty() && self.younger.is_empty()
}
}
rust
assert!(q.is_empty());
q.push('☉');
assert!(!q.is_empty());
rust
impl Queue {
pub fn split(self) -> (Vec<char>, Vec<char>) {
(self.older, self.younger)
}
}
rust
let mut q = Queue { older: Vec::new(), younger: Vec::new() };
rust
q.push('P');
q.push('D');
assert_eq!(q.pop(), Some('P'));
q.push('X');
let (older, younger) = q.split();
// q is now uninitialized.
assert_eq!(older, vec!['D']);
assert_eq!(younger, vec!['X'])
rust
()
plaintext
q
rust
[E0425] Error: cannot find value `q` in this scope
   ╭─[command_23:1:1]

 1 │ q
   │ ┬
   │ ╰── error: cannot find value `q` in this scope
   │ │
   │ ╰── help: a unit struct with a similar name exists: `A`
───╯
plaintext
impl Queue {
    fn new() -> Queue {
        Queue { older: Vec::new(), younger: Vec::new() }
    }
}
rust
let mut bq = Box::new(Queue::new());
bq.push('A')
rust
()
plaintext
use std::rc::Rc;
rust
struct Node {
tag: String,
children: Vec<Rc<Node>>
}
rust
impl Node {
    fn new(tag: &str) ->Node {
        Node {
            tag: tag.to_string(),
            children: vec![],
        }
    }
}
rust
impl Node {
fn append_to(self: Rc<Self>, parent: &mut Node) {
parent.children.push(self);
}
}
rust
let shared_node = Rc::new(Node::new("first"));
rust
pub struct Vector2 {
x: f32,
y: f32,
}
rust
impl Vector2 {
const ZERO: Vector2 = Vector2 { x: 0.0, y: 0.0 };
const UNIT: Vector2 = Vector2 { x: 1.0, y: 0.0 };
}
rust
let scaled = Vector2::UNIT.x;
rust

Generic Structs#

pub struct TheQueue<T> {
older: Vec<T>,
younger: Vec<T>
}
rust
impl<T> TheQueue<T> {
pub fn new() -> TheQueue<T> {
TheQueue { older: Vec::new(), younger: Vec::new() }
}
pub fn push(&mut self, t: T) {
self.younger.push(t);
}
pub fn is_empty(&self) -> bool {
self.older.is_empty() && self.younger.is_empty()
}
}
rust
impl TheQueue<f64> {
    fn sum(&self) -> f64 {
        0_f64
    }
}
rust
let mut q = TheQueue::<char>::new();
rust
q.push('a');
rust

Structs with Lifetime Parameters#

struct Extrema<'elt> {
    greatest: &'elt i32,
    least: &'elt i32,
}
rust
fn find_extrema<'s>(slice: &'s [i32]) -> Extrema<'s> {
let mut greatest = &slice[0];
let mut least = &slice[0];
for i in 1..slice.len() {
if slice[i] < *least { least = &slice[i]; }
if slice[i] > *greatest { greatest = &slice[i]; }
}
Extrema { greatest, least }
}
rust

Deriving Common Traits for Struct Types#

#[derive(Copy, Clone, Debug, PartialEq)]
struct Point {
x: f64,
y: f64
}
rust
{
    use std::cell::RefCell;
let ref_cell: RefCell<String> =  RefCell::new("hello".to_string());
let r = ref_cell.borrow();
let count = r.len();
assert_eq!(count,5);
let mut w = ref_cell.borrow_mut();
w.push_str("world");
}
rust
thread '<unnamed>' panicked at src/lib.rs:247:22:
already borrowed: BorrowMutError
stack backtrace:
   0: rust_begin_unwind
             at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/std/src/panicking.rs:597:5
   1: core::panicking::panic_fmt
             at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/core/src/panicking.rs:72:14
   2: core::cell::panic_already_borrowed
             at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/core/src/cell.rs:762:5
   3: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
   4: run_user_code_39
   5: evcxr::runtime::Runtime::run_loop
   6: evcxr::runtime::runtime_hook
   7: evcxr_jupyter::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
plaintext

Enums and Patterns#

use std::cmp::Ordering;
fn compare(a: i32,b: i32) -> Ordering {
    if a > b {
        Ordering::Greater
    } else if a < b {
        Ordering::Less
    } else {
        Ordering::Equal
    }
}
rust
enum HttpStatus {
Ok = 200,
NotModified = 304,
NotFound = 404,
}
rust
use std::mem::size_of;
assert_eq!(size_of::<Ordering>(), 1);
assert_eq!(size_of::<HttpStatus>(), 2); // 404 doesn't fit in a u8
rust
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum TimeUnit {
Seconds, Minutes, Hours, Days, Months, Years,
}
rust

Enums in Memory#

image.png

Patterns#

enum RoughTime {
InThePast(TimeUnit, u32),
JustNow,
InTheFuture(TimeUnit, u32),
}
rust
fn rough_time_to_english(rt: RoughTime) -> String {
 match rt {
 RoughTime::InThePast(units, count) =>
 format!("{} {} ago", count, units.plural()),
 RoughTime::JustNow =>
 format!("just now"),
 RoughTime::InTheFuture(units, count) =>
 format!("{} {} from now", count, units.plural()),
 }
}
rust

image.png

fn greet_people(names: &[&str]) {
match names {
[] => { println!("Hello, nobody.") },
[a] => { println!("Hello, {}.", a) },
[a, b] => { println!("Hello, {} and {}.", a, b) },
[a, .., b] => { println!("Hello, everyone from {} to {}.", a, b) }
}
}
rust

image.png image.png

// An ordered collection of `T`s.
enum BinaryTree<T> {
Empty,
NonEmpty(Box<TreeNode<T>>),
}
// A part of a BinaryTree.
struct TreeNode<T> {
element: T,
left: BinaryTree<T>,
right: BinaryTree<T>,
}
rust

Structs#

LISP programmers know the value of everything, but the cost of nothing. —Alan Perlis, epigram image.png > image.png > image.png

Error Handling#

I knew if I stayed around long enough, something like this would happen. —George Bernard Shaw on dying

use std::error::Error;
use std::io::{Write,stderr};
fn print_error(mut err: &dyn Error) {
    let _ = writeln!(stderr(),"error: {}",err);
    while let Some(source) = err.source() {
        let _ = writeln!(stderr(),"cased by :{}",source);
        err = source;
    }
}
rust
let weather = match get_weather(hometown) {
Ok(success_value) => success_value,
Err(err) => return Err(err)
};
rust

Crates and Modules#

image.png

image.png image.png image.png

bin#

image.png

Attributes#

image.png image.png

Test#

Documentation#

image.png

Package Versions#

image.png

rust

Input and Output#

image.png

image.png

use std::io;
use std::io::prelude::*;
fn grep(target: &str) -> io::Result<()> {
    let stdin = io::stdin();
    for line_result in stdin.lock().lines() {
        let line = line_result?;
        if line.contains(target) {
            println!("{}",line);
        }
    }
    Ok(())
}
rust
fn grep<R>(target: &str, reader: R) -> io::Result<()>
where R: BufRead
{
for line_result in reader.lines() {
let line = line_result?;
if line.contains(target) {
println!("{}", line);
}
}
Ok(())
}
rust
// let stdin = io::stdin();
// grep(&target, stdin.lock())?; // ok
// let f = File::open(file)?;
// grep(&target, BufReader::new(f))?; // also o
rust
use std::fs::OpenOptions;
let log = OpenOptions::new()
    .append(true)
    .open("server.log").unwrap_err();
let file = OpenOptions::new()
    .write(true)
    .create_new(true)
    .open("new_file.log").unwrap_err();
rust
thread '<unnamed>' panicked at src/lib.rs:138:27:
called `Result::unwrap_err()` on an `Ok` value: File { fd: 3, path: "/home/realcpf/Documents/rustRepos/rust-prog-2021/new_file.log", read: false, write: true }
stack backtrace:
   0: rust_begin_unwind
             at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/std/src/panicking.rs:597:5
   1: core::panicking::panic_fmt
             at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/core/src/panicking.rs:72:14
   2: core::result::unwrap_failed
             at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/core/src/result.rs:1652:5
   3: <unknown>
   4: <unknown>
   5: evcxr::runtime::Runtime::run_loop
   6: evcxr::runtime::runtime_hook
   7: evcxr_jupyter::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
plaintext
use std::process::{Command, Stdio};
let mut child =
Command::new("grep")
.arg("-e")
.arg("a.*e.*i.*o.*u")
.stdin(Stdio::piped())
.spawn()?;
let mut to_child = child.stdin.take().unwrap();
for word in my_words {
writeln!(to_child, "{}", word)?;
}
drop(to_child); // close grep's stdin, so it will exit
child.wait()?;
rust
[E0425] Error: cannot find value `my_words` in this scope
   ╭─[command_12:1:1]

 9 │ for word in my_words {
   │             ────┬───
   │                 ╰───── not found in this scope
───╯
plaintext

image.png

use std::path::Path;
let home_dir = Path::new("/home/realcpf");
rust
assert_eq!(home_dir.parent(),Some(Path::new("/home")));
rust
use std::ffi::OsStr;
assert_eq!(Path::new("/home/fwolfe/program.txt").file_name(),
Some(OsStr::new("program.txt")));
rust
let path1 = Path::new("/usr/share/dict");
assert_eq!(path1.join("words"),
Path::new("/usr/share/dict/words"));
rust
let file = Path::new("/home/jimb/calendars/calendar-18x18.pdf");
assert_eq!(file.ancestors().collect::<Vec<_>>(),
vec![Path::new("/home/jimb/calendars/calendar-18x18.pdf"),
Path::new("/home/jimb/calendars"),
Path::new("/home/jimb"),
Path::new("/home"),
Path::new("/")])
rust
()
plaintext

image.png image.png

#[cfg(unix)]
use std::os::unix::fs::symlink;
/// Stub implementation of `symlink` for platforms that don't provide it.
#[cfg(not(unix))]
fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, _dst: Q)
-> std::io::Result<()>
{
Err(io::Error::new(io::ErrorKind::Other,
format!("can't copy symbolic link: {}",
src.as_ref().display())))
}
rust

Strings and Text#

image.png

assert_eq!("うどん: udon".as_bytes(),
&[0xe3, 0x81, 0x86, //う
0xe3, 0x81, 0xa9, //ど
0xe3, 0x82, 0x93, //ん
0x3a, 0x20, 0x75, 0x64, 0x6f, 0x6e // : udon
]);
rust
assert_eq!(" ערבטוב ".chars().next(), Some('ע'));
rust
assert_eq!("カニ".chars().next(), Some('カ'))
rust
()
plaintext

image.png image.png image.png

assert!(32u8.is_ascii_whitespace());
assert!(b'9'.is_ascii_digit())
rust
()
plaintext
// char::is_ascii_whitespace函数实现了许多Web标准通用的空格定义,
// 而char::is_whitespace遵循Unicode标准。

let line_tab = '\u{000b}'; // 'line tab', AKA 'vertical tab'
assert_eq!(line_tab.is_whitespace(), true);
assert_eq!(line_tab.is_ascii_whitespace(), false)
rust
()
plaintext
assert_eq!('F'.to_digit(16), Some(15));
assert_eq!(std::char::from_digit(15, 16), Some('f'));
assert!(char::is_digit('f', 16));
rust
let mut upper = 's'.to_uppercase();
assert_eq!(upper.next(), Some('S'));
assert_eq!(upper.next(), None);
rust
// The uppercase form of the German letter "sharp S" is "SS":
let mut upper = 'ß'.to_uppercase();
assert_eq!(upper.next(), Some('S'));
assert_eq!(upper.next(), Some('S'));
assert_eq!(upper.next(), None);
rust
// Unicode says to lowercase Turkish dotted capital 'İ' to 'i'
// followed by `'\u{307}'`, COMBINING DOT ABOVE, so that a
// subsequent conversion back to uppercase preserves the dot.
let ch = 'İ'; // `'\u{130}'`
let mut lower = ch.to_lowercase();
assert_eq!(lower.next(), Some('i'));
assert_eq!(lower.next(), Some('\u{307}'));
assert_eq!(lower.next(), None)
rust
()
plaintext
assert_eq!('B' as u32, 66);
assert_eq!('饂' as u8, 66); // upper bits truncated
assert_eq!('二' as i8, -116); // same
rust
assert_eq!(char::from(66), 'B');
assert_eq!(std::char::from_u32(0x9942), Some('饂'));
assert_eq!(std::char::from_u32(0xd800), None); // reserved for UTF-16
rust

image.png

let spacey = "man hat tan";
let spaceless: String =
spacey.chars().filter(|c| !c.is_whitespace()).collect();
assert_eq!(spaceless, "manhattan");
rust
let full = "bookkeeping";
assert_eq!(&full[..4], "book");
assert_eq!(&full[5..], "eeping");
assert_eq!(&full[2..4], "ok");
assert_eq!(full[..].len(), 11);
assert_eq!(full[5..].contains("boo"), false)
rust
()
plaintext
let parenthesized = "Rust (饂)";
assert_eq!(parenthesized[6..].chars().next(), Some('饂'));
rust
let mut also_spaceless = "con".to_string();
also_spaceless.extend("tri but ion".split_whitespace());
assert_eq!(also_spaceless, "contribution");
rust
use std::fmt::Write;
let mut letter = String::new();
writeln!(letter, "Whose {} these are I think I know", "rutabagas")?;
writeln!(letter, "His house is in the village though;")?;
assert_eq!(letter, "Whose rutabagas these are I think I know\n\
His house is in the village though;\n");
rust
let left = "partners".to_string();
let mut right = "crime".to_string();
assert_eq!(left + " in " + &right, "partners in crime");
right += " doesn't pay";
assert_eq!(right, "crime doesn't pay")
rust
()
plaintext
// 返回给定字节索引范围内的迭代器,并在删除迭代器后删除字符。范围后的字符向前面移动
let mut choco = "chocolate".to_string();
assert_eq!(choco.drain(3..6).collect::<String>(), "col");
assert_eq!(choco, "choate");
rust
let mut beverage = "a piña colada".to_string();
beverage.replace_range(2..7, "kahlua"); // 'ñ' is two bytes!
assert_eq!(beverage, "a kahlua colada");
rust
let haystack = "One fine day, in the middle of the night";
assert_eq!(haystack.find(','), Some(12));
assert_eq!(haystack.find("night"), Some(35));
assert_eq!(haystack.find(char::is_whitespace), Some(3));
rust
assert_eq!("## Elephants"
.trim_start_matches(|ch: char| ch == '#' || ch.is_whitespace()),
"Elephants");
rust
let code = "\t function noodle() { ";
assert_eq!(code.trim_start_matches([' ', '\t'].as_ref()),
"function noodle() { ");
// Shorter equivalent: &[' ', '\t'][..]
rust
assert!("2017".starts_with(char::is_numeric));
rust
let quip = "We also know there are known unknowns";
assert_eq!(quip.find("know"), Some(8));
assert_eq!(quip.rfind("know"), Some(31));
assert_eq!(quip.find("ya know"), None);
assert_eq!(quip.rfind(char::is_uppercase), Some(0));
rust
assert_eq!("The only thing we have to fear is fear itself"
.replace("fear", "spin"),
"The only thing we have to spin is spin itself");
assert_eq!("`Borrow` and `BorrowMut`"
.replace(|ch:char| !ch.is_alphanumeric(), ""),
"BorrowandBorrowMut");
rust

image.png

assert_eq!("élan".char_indices().collect::<Vec<_>>(),
vec![(0, 'é'), // has a two-byte UTF-8 encoding
(2, 'l'),
(3, 'a'),
(4, 'n')]);
rust
assert_eq!("élan".bytes().collect::<Vec<_>>(),
vec![195, 169, b'l', b'a', b'n']);
rust
// The ':' characters are separators here. Note the final "".
assert_eq!("jimb:1000:Jim Blandy:".split(':').collect::<Vec<_>>(),
vec!["jimb", "1000", "Jim Blandy", ""]);
// The '\n' characters are terminators here.
assert_eq!("127.0.0.1 localhost\n\
127.0.0.1 www.reddit.com\n"
.split_terminator('\n').collect::<Vec<_>>(),
vec!["127.0.0.1 localhost",
"127.0.0.1 www.reddit.com"]);
// Note, no final ""!
rust
let poem = "This is just to say\n\
I have eaten\n\
the plums\n\
again\n";
assert_eq!(poem.split_whitespace().collect::<Vec<_>>(),
vec!["This", "is", "just", "to", "say",
"I", "have", "eaten", "the", "plums",
"again"]);
rust
assert_eq!("\t*.rs ".trim(), "*.rs");
assert_eq!("\t*.rs ".trim_start(), "*.rs ");
assert_eq!("\t*.rs ".trim_end(), "\t*.rs");
rust
assert_eq!("001990".trim_start_matches('0'), "1990");
rust
use std::str::FromStr;
assert_eq!(usize::from_str("3628800"), Ok(3628800));
assert_eq!(f64::from_str("128.5625"), Ok(128.5625));
assert_eq!(bool::from_str("true"), Ok(true));
assert!(f64::from_str("not a float at all").is_err());
assert!(bool::from_str("TRUE").is_err());
rust
assert_eq!(char::from_str("é"), Ok('é'));
assert!(char::from_str("abcdefg").is_err());
rust
use std::net::IpAddr;
let address = IpAddr::from_str("fe80::0000:3ea9:f4ff:fe34:7a50")?;
assert_eq!(address,
IpAddr::from([0xfe80, 0, 0, 0, 0x3ea9, 0xf4ff, 0xfe34, 0x7a50]));
rust
let address = "fe80::0000:3ea9:f4ff:fe34:7a50".parse::<IpAddr>().unwrap();
rust
assert_eq!(format!("{}, wow", "doge"), "doge, wow");
assert_eq!(format!("{}", true), "true");
assert_eq!(format!("({:.3}, {:.3})", 0.5, f64::sqrt(3.0)/2.0),
"(0.500, 0.866)");
// Using `address` from above.
let formatted_addr: String = format!("{}", address);
assert_eq!(formatted_addr, "fe80::3ea9:f4ff:fe34:7a50");
rust

Producing Text from UTF-8 Data#

let good_utf8: Vec<u8> = vec![0xe9, 0x8c, 0x86];
assert_eq!(String::from_utf8(good_utf8).ok(), Some("錆".to_string()));
let bad_utf8: Vec<u8> = vec![0x9f, 0xf0, 0xa6, 0x80];
let result = String::from_utf8(bad_utf8);
assert!(result.is_err());
// Since String::from_utf8 failed, it didn't consume the original
// vector, and the error value hands it back to us unharmed.
assert_eq!(result.unwrap_err().into_bytes(),
vec![0x9f, 0xf0, 0xa6, 0x80])
rust
()
plaintext
fn get_name() -> String {
std::env::var("USER") // Windows uses "USERNAME"
.unwrap_or("whoever you are".to_string())
}
println!("Greetings, {}!", get_name())
rust
Greetings, realcpf!

()
plaintext
use std::borrow::Cow;
fn get_name() -> Cow<'static ,str> {
    std::env::var("USER")
    .map(|v|Cow::Owned(v))
    .unwrap_or(Cow::Borrowed("whover you are"))
}
println!("Greetings, {}!", get_name())
rust
Greetings, realcpf!

()
plaintext

Formatting Values#

println!("{:.3}μs: relocated {} at {:#x} to {:#x}, {} bytes",
0.84391, "object",
140737488346304_usize, 6299664_usize, 64);
rust
0.844μs: relocated object at 0x7fffffffdcc0 to 0x602010, 64 bytes
plaintext

image.png

assert_eq!(format!("{{a, c}} ⊂ {{a, b, c}}"),
"{a, c} ⊂ {a, b, c}");
rust

Formatting Text Values#

image.png

assert_eq!(format!("{:4}", "th\u{e9}"), "th\u{e9} ");
assert_eq!(format!("{:4}", "the\u{301}"), "the\u{301}");
rust

Formatting Numbers#

image.png image.png

use std::collections::HashMap;
let mut map = HashMap::new();
map.insert("Portland", (45.5237606,-122.6819273));
map.insert("Taipei", (25.0375167, 121.5637));
println!("{:?}", map);
rust
{"Taipei": (25.0375167, 121.5637), "Portland": (45.5237606, -122.6819273)}
plaintext
println!("ordinary: {:02?}", [9, 15, 240]);
println!("hex: {:02x?}", [9, 15, 240]);
rust
ordinary: [09, 15, 240]
hex: [09, 0f, f0]
plaintext
#[derive(Copy, Clone, Debug)]
struct Complex { re: f64, im: f64 }
let third = Complex { re: -0.5, im: f64::sqrt(0.75) };
println!("{:?}", third)
rust
Complex { re: -0.5, im: 0.8660254037844386 }

()
plaintext

Formatting Pointers for Debugging#

use std::rc::Rc;
let original = Rc::new("mazurka".to_string());
let cloned = original.clone();
let impostor = Rc::new("mazurka".to_string());
println!("text: {}, {}, {}", original, cloned, impostor);
println!("pointers: {:p}, {:p}, {:p}", original, cloned, impostor);
rust
text: mazurka, mazurka, mazurka
pointers: 0x55ab5c90f080, 0x55ab5c90f080, 0x55ab5c917540
plaintext
assert_eq!(format!("{1},{0},{2}", "zeroth", "first", "second"),
"first,zeroth,second");
assert_eq!(format!("{2:#06x},{1:b},{0:=>10}", "first", 10, 100),
"0x0064,1010,=====first");
rust
format!("{description:.<25}{quantity:2} @ {price:5.2}",
price=3.25,
quantity=3,
description="Maple Turmeric Latte")
rust
"Maple Turmeric Latte..... 3 @  3.25"
plaintext
format!("{mode} {2} {} {}",
"people", "eater", "purple", mode="flying")
rust
"flying purple people eater"
plaintext

image.png

use std::fmt;
impl fmt::Display for Complex {
fn fmt(&self, dest: &mut fmt::Formatter) -> fmt::Result {
let im_sign = if self.im < 0.0 { '-' } else { '+' };
write!(dest, "{} {} {}i", self.re, im_sign, f64::abs(self.im))
}
}
rust
let one_twenty = Complex { re: -0.5, im: 0.866 };
assert_eq!(format!("{}", one_twenty),
"-0.5 + 0.866i");
let two_forty = Complex { re: -0.5, im: -0.866 };
assert_eq!(format!("{}", two_forty),
"-0.5 - 0.866i");
rust
rust

Fundamental-Types#

There are many, many types of books in the world, which makes good sense, because there are many, many types of people, and everybody wants to read something different. —Lemony Snicke image.png > image.png > image.png

Fixed-Width Numeric Types#

image.png image.png

// 10_u8.checked_mul(10).expect("数值溢出");
// 100_u8.checked_add(200).expect("");
// let x = 100_u8;
// let y = 100_u8;
// // Do the addition; panic if it overflows.
// let sum = x.checked_add(y).unwrap();
// 超出不报错
500_u16.wrapping_mul(500);  // 53392
500_i16.wrapping_mul(500); // -12144
// In bitwise shift operations, the shift distance
// is wrapped to fall within the size of the value.
// So a shift of 17 bits in a 16-bit type is a shift
// of 1.
// assert_eq!(5_i16.wrapping_shl(17), 10);
rust

image.png image.png

Characters#

assert_eq!('*' as i32, 42);
assert_eq!('ಠ' as u16, 0xca0);
assert_eq!('ಠ' as i8, -0x60); // U+0CA0 truncated to eight bits, signed
rust
assert_eq!('*'.is_alphabetic(), false);
assert_eq!('β'.is_alphabetic(), true);
assert_eq!('8'.to_digit(10), Some(8));
assert_eq!('ಠ'.len_utf8(), 3);
assert_eq!(std::char::from_digit(2, 10), Some('2'));
rust

Tuples#

let text = "I see the eigenvalue in thine eye";
let (head, tail) = text.split_at(21);
assert_eq!(head, "I see the eigenvalue ");
assert_eq!(tail, "in thine eye");
rust
let text = "I see the eigenvalue in thine eye";
let temp = text.split_at(21);
let head = temp.0;
let tail = temp.1;
assert_eq!(head, "I see the eigenvalue ");
assert_eq!(tail, "in thine eye");
rust

Slices#

let v: Vec<f64> = vec![0.0, 0.707, 1.0, 0.707];
let a: [f64; 4] = [0.0, -0.707, -1.0, -0.707];
let sv: &[f64] = &v;
let sa: &[f64] = &a;
rust

image.png

String Literals#

println!("In the room the women come and go,
Singing of Mount Abora");
println!("It was a bright, cold day in April, and \
there were four of us—\
more or less.");
let default_win_install_path = r"C:\Program Files\Gorillas";
println!(r###"
This raw string started with 'r###"'.
Therefore it does not end until we reach a quote mark ('"')
followed immediately by three pound signs ('###'):
"###);
rust
In the room the women come and go,
Singing of Mount Abora
It was a bright, cold day in April, and there were four of us—more or less.

This raw string started with 'r###"'.
Therefore it does not end until we reach a quote mark ('"')
followed immediately by three pound signs ('###'):
plaintext

Byte Strings#

let method = b"GET";
assert_eq!(method, &[b'G', b'E', b'T']);
rust

Strings#

let noodles = "noodles".to_string();
let oodles = &noodles[1..];
let poodles = "ಠ_ಠ";
rust
let oodles = &noodles[1..];
let oodles = &noodles[1..];
    ^^^^^^
The variable `oodles` contains a reference with a non-static lifetime so
can't be persisted. You can prevent this error by making sure that the
variable goes out of scope - i.e. wrapping the code in {}.
plaintext

image.png

assert_eq!("ಠ_ಠ".len(), 7);
assert_eq!("ಠ_ಠ".chars().count(), 3)
rust
()
plaintext

image.png

let error_message = "too many pets".to_string();
rust
println!("{}",error_message)
rust
too many pets

()
plaintext

format string#

println!("{}",format!("{}°{:02}′{:02}″N", 24, 5, 23))
rust
24°05′23″N

()
plaintext
let bits = vec!["veni", "vidi", "vici"];
rust
bits.concat()
rust
"venividivici"
plaintext
bits.join(",")
rust
"veni,vidi,vici"
plaintext
"ONE".to_lowercase()
rust
"one"
plaintext
"peanut".contains("nut")
rust
true
plaintext
"ಠ_ಠ".replace("ಠ", "■")
rust
"■_■"
plaintext
rust

Iterator#

// You should usually use HashSet, but its iteration order is
// nondeterministic, so BTreeSet works better in examples.
use std::collections::BTreeSet;
let mut favorites = BTreeSet::new();
favorites.insert("Lucy in the Sky With Diamonds".to_string());
favorites.insert("Liebesträume No. 3".to_string());
let mut it = favorites.into_iter();
assert_eq!(it.next(), Some("Liebesträume No. 3".to_string()));
assert_eq!(it.next(), Some("Lucy in the Sky With Diamonds".to_string()));
assert_eq!(it.next(), None);
rust
use rand::random; // In Cargo.toml dependencies: rand = "0.7"
use std::iter::from_fn;
// Generate the lengths of 1000 random line segments whose endpoints
// are uniformly distributed across the interval [0, 1]. (This isn't a
// distribution you're going to find in the `rand_distr` crate, but
// it's easy to make yourself.)
let lengths: Vec<f64> =
from_fn(|| Some((random::<f64>() - random::<f64>()).abs()))
.take(1000)
.collect();
rust
[E0432] Error: unresolved import `rand`
plaintext
fn fibonacci() -> impl Iterator<Item = usize> {
    let mut state = (0,1);
    std::iter::from_fn(move || {
        state = (state.1,state.0 + state.1);
        Some(state.0)
    })
}
rust
assert_eq!(fibonacci().take(8).collect::<Vec<_>>(),
          vec![1, 1, 2, 3, 5, 8, 13, 21])
rust
()
plaintext

drain Methods#

use std::iter::FromIterator;
let mut outer = "Earth".to_string();
let inner = String::from_iter(outer.drain(1..4));
assert_eq!(outer, "Eh");
assert_eq!(inner, "art")
rust
()
plaintext

Iterator Sources#

image.png image.png

map and filter#

{
    let text = " ponies \n giraffes\niguanas \nsquid".to_string();
    let v: Vec<&str> = text.lines()
    .map(str::trim)
    .collect();
    assert_eq!(v, ["ponies", "giraffes", "iguanas", "squid"]);
}
rust
()
plaintext
{
    let text = " ponies \n giraffes\niguanas \nsquid".to_string();
    let v: Vec<&str> = text.lines()
    .map(str::trim)
    .filter(|s| *s != "iguanas")
    .collect();
    assert_eq!(v, ["ponies", "giraffes", "squid"]);
}
rust
()
plaintext

filter_map and flat_map#

use std::str::FromStr;
let text = "1\nfrond .25 289\n3.1415 estuary\n";
for number in text.split_whitespace()
    .filter_map(|w| f64::from_str(w).ok()){
    println!("{:4.2}",number.sqrt());
}
rust
1.00
0.50
17.00
1.77

()
plaintext
use std::collections::HashMap;
let mut major_cities = HashMap::new();
major_cities.insert("Japan", vec!["Tokyo", "Kyoto"]);
major_cities.insert("The United States", vec!["Portland", "Nashville"]);
major_cities.insert("Brazil", vec!["São Paulo", "Brasília"]);
major_cities.insert("Kenya", vec!["Nairobi", "Mombasa"]);
major_cities.insert("The Netherlands", vec!["Amsterdam", "Utrecht"]);
let countries = ["Japan", "Brazil", "Kenya"];
for &city in countries.iter().flat_map(|country| &major_cities[country]) {
println!("{}", city);
}
rust
Tokyo
Kyoto
São Paulo
Brasília
Nairobi
Mombasa

()
plaintext

flatten#

use std::collections::BTreeMap;
// A table mapping cities to their parks: each value is a vector.
let mut parks = BTreeMap::new();
parks.insert("Portland", vec!["Mt. Tabor Park", "Forest Park"]);
parks.insert("Kyoto", vec!["Tadasu-no-Mori Forest", "Maruyama Koen"]);
parks.insert("Nashville", vec!["Percy Warner Park", "Dragon Park"]);
rust
use std::collections::BTreeMap;
// A table mapping cities to their parks: each value is a vector.
let mut parks = BTreeMap::new();
parks.insert("Portland", vec!["Mt. Tabor Park", "Forest Park"]);
parks.insert("Kyoto", vec!["Tadasu-no-Mori Forest", "Maruyama Koen"]);
parks.insert("Nashville", vec!["Percy Warner Park", "Dragon Park"]);
// Build a vector of all parks. `values` gives us an iterator producing
// vectors, and then `flatten` produces each vector's elements in turn.
let all_parks: Vec<_> = parks.values().flatten().cloned().collect();
assert_eq!(all_parks,
vec!["Tadasu-no-Mori Forest", "Maruyama Koen", "Percy Warner Park",
"Dragon Park", "Mt. Tabor Park", "Forest Park"]);
rust

take and take_while#

let message = "To: jimb\r\n\
From: superego <editor@oreilly.com>\r\n\
\r\n\
Did you get any writing done today?\r\n\
When will you stop wasting time plotting fractals?\r\n";
for header in message.lines().take_while(|l| !l.is_empty()) {
println!("{}" , header);
}
rust
To: jimb
From: superego <editor@oreilly.com>

()
plaintext

skip and skip_while#

for body in message.lines()
.skip_while(|l| !l.is_empty())
.skip(1) {
println!("{}" , body);
}
rust
Did you get any writing done today?
When will you stop wasting time plotting fractals?

()
plaintext

peekable#

use std::iter::Peekable;
fn parse_number<I>(tokens: &mut Peekable<I>) -> u32
    where I:Iterator<Item = char>{
    let mut n = 0;
    loop {
        match tokens.peek() {
            Some(r) if r.is_digit(10) => {
                n = n * 10 + r.to_digit(10).unwrap();
            }
            _ => return  n
        }
        tokens.next();
    }
}
rust
let mut chars = "226153980,1766319049".chars().peekable();
assert_eq!(parse_number(&mut chars), 226153980);
// Look, `parse_number` didn't consume the comma! So we will.
assert_eq!(chars.next(), Some(','));
assert_eq!(parse_number(&mut chars), 1766319049);
assert_eq!(chars.next(), None);
rust

fuse#

struct Flaky(bool);
impl Iterator for Flaky {
type Item = &'static str;
fn next(&mut self) -> Option<Self::Item> {
if self.0 {
self.0 = false;
Some("totally the last item")
} else {
self.0 = true; // D'oh!
None
}
}
}
rust
let mut flaky = Flaky(true);
assert_eq!(flaky.next(), Some("totally the last item"));
assert_eq!(flaky.next(), None);
assert_eq!(flaky.next(), Some("totally the last item"));
let mut not_flaky = Flaky(true).fuse();
assert_eq!(not_flaky.next(), Some("totally the last item"));
assert_eq!(not_flaky.next(), None);
assert_eq!(not_flaky.next(), None);
rust

Reversible Iterators and rev#

{
    let bee_parts = ["head", "thorax", "abdomen"];
    let mut iter = bee_parts.iter();
    assert_eq!(iter.next(), Some(&"head"));
    assert_eq!(iter.next_back(), Some(&"abdomen"));
    assert_eq!(iter.next(), Some(&"thorax"));
    assert_eq!(iter.next_back(), None);
    assert_eq!(iter.next(), None);
}
rust
()
plaintext
// fn rev(self) -> impl Iterator<Item=Self>
// where Self: Sized + DoubleEndedIterator;
rust
{
    let meals = ["breakfast", "lunch", "dinner"];
    let mut iter = meals.iter().rev();
    assert_eq!(iter.next(), Some(&"dinner"));
    assert_eq!(iter.next(), Some(&"lunch"));
    assert_eq!(iter.next(), Some(&"breakfast"));
    assert_eq!(iter.next(), None);
}
rust
()
plaintext

inspect#

let upper_case: String = "große".chars()
.inspect(|c| println!("before: {:?}", c))
.flat_map(|c| c.to_uppercase())
.inspect(|c| println!(" after: {:?}", c))
.collect();
assert_eq!(upper_case, "GROSSE");
rust
before: 'g'
 after: 'G'
before: 'r'
 after: 'R'
before: 'o'
 after: 'O'
before: 'ß'
 after: 'S'
 after: 'S'
before: 'e'
 after: 'E'
plaintext

chain#

let v: Vec<i32> = (1..4).chain(vec![20, 30, 40]).collect();
assert_eq!(v, [1, 2, 3, 20, 30, 40]);
rust
let v: Vec<i32> = (1..4).chain(vec![20, 30, 40]).rev().collect();
assert_eq!(v, [40, 30, 20, 3, 2, 1]);
rust

enumerate#

let vlist = vec!['A','B','C','D'];
for (i,c) in vlist.into_iter().enumerate() {
    println!("{}--{}",i,c);
}
rust
0--A
1--B
2--C
3--D

()
plaintext

zip#

let v:Vec<_> = (0..).zip("ABCD".chars()).collect();
assert_eq!(v, vec![(0, 'A'), (1, 'B'), (2, 'C'), (3, 'D')]);
rust
use std::iter::repeat;
let endings = vec!["once", "twice", "chicken soup with rice"];
let rhyme: Vec<_> = repeat("going")
.zip(endings)
.collect();
assert_eq!(rhyme, vec![("going", "once"),
("going", "twice"),
("going", "chicken soup with rice")]);
rust

by_ref#

let message = "To: jimb\r\n\
From: id\r\n\
\r\n\
    Oooooh, donuts!!\r\n";
let mut lines = message.lines();
println!("Headers:");
for header in lines.by_ref().take_while(|l| !l.is_empty()) {
println!("{}" , header);
}
println!("\nBody:");
for body in lines {
println!("{}" , body);
}
rust
Headers:
To: jimb
From: id

Body:
Oooooh, donuts!!

()
plaintext

cloned, copied#

let a = ['1', '2', '3', '∞'];
assert_eq!(a.iter().next(), Some(&'1'));
assert_eq!(a.iter().cloned().next(), Some('1'));
rust

cycle#

{
    let dirs = ["North", "East", "South", "West"];
let mut spin = dirs.iter().cycle();
assert_eq!(spin.next(), Some(&"North"));
assert_eq!(spin.next(), Some(&"East"));
assert_eq!(spin.next(), Some(&"South"));
assert_eq!(spin.next(), Some(&"West"));
assert_eq!(spin.next(), Some(&"North"));
assert_eq!(spin.next(), Some(&"East"))
}
rust
()
plaintext
use std::iter::{once, repeat};
{
    let fizzes = repeat("").take(2).chain(once("fizz")).cycle();
    let buzzes = repeat("").take(4).chain(once("buzz")).cycle();
    let fizzes_buzzes = fizzes.zip(buzzes);
    let fizz_buzz = (1..100).zip(fizzes_buzzes)
    .map(|tuple|
    match tuple {
    (i, ("", "")) => i.to_string(),
    (_, (fizz, buzz)) => format!("{}{}", fizz, buzz)
    });
    for line in fizz_buzz {
    println!("{}", line);
    }
}
rust

Simple Accumulation: count, sum, product#

use std::cmp::Ordering;
fn cmp(lhs: &f64,rhs:&f64) -> Ordering {
    lhs.partial_cmp(rhs).unwrap()
}
let numbers = [1.0, 4.0, 2.0];
assert_eq!(numbers.iter().copied().max_by(cmp), Some(4.0));
assert_eq!(numbers.iter().copied().min_by(cmp), Some(1.0));
let numbers = [1.0, 4.0, std::f64::NAN, 2.0];
assert_eq!(numbers.iter().copied().max_by(cmp), Some(4.0)); // panics
rust
thread '<unnamed>' panicked at src/lib.rs:8:26:
called `Option::unwrap()` on a `None` value
stack backtrace:
   0: rust_begin_unwind
             at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/std/src/panicking.rs:597:5
   1: core::panicking::panic_fmt
             at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/core/src/panicking.rs:72:14
   2: core::panicking::panic
             at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/core/src/panicking.rs:127:5
   3: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
   4: run_user_code_29
   5: evcxr::runtime::Runtime::run_loop
   6: evcxr::runtime::runtime_hook
   7: evcxr_jupyter::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
plaintext

max_by_key, min_by_key#

use std::collections::HashMap;
let mut populations = HashMap::new();
populations.insert("Portland", 583_776);
populations.insert("Fossil", 449);
populations.insert("Greenhorn", 2);
populations.insert("Boring", 7_762);
populations.insert("The Dalles", 15_340);
assert_eq!(populations.iter().max_by_key(|&(_name, pop)| pop),
Some((&"Portland", &583_776)));
assert_eq!(populations.iter().min_by_key(|&(_name, pop)| pop),
Some((&"Greenhorn", &2)));
rust

Comparing Item Sequences#

let packed = "Helen of Troy";
let spaced = "Helen of Troy";
let obscure = "Helen of Sandusky"; // nice person, just not famous
assert!(packed != spaced);
assert!(packed.split_whitespace().eq(spaced.split_whitespace()));
// This is true because ' ' < 'o'.
assert!(spaced < obscure);
// This is true because 'Troy' > 'Sandusky'.
assert!(spaced.split_whitespace().gt(obscure.split_whitespace()));
rust
thread '<unnamed>' panicked at src/lib.rs:178:1:
assertion failed: packed != spaced
stack backtrace:
   0: rust_begin_unwind
             at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/std/src/panicking.rs:597:5
   1: core::panicking::panic_fmt
             at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/core/src/panicking.rs:72:14
   2: core::panicking::panic
             at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/core/src/panicking.rs:127:5
   3: <unknown>
   4: <unknown>
   5: evcxr::runtime::Runtime::run_loop
   6: evcxr::runtime::runtime_hook
   7: evcxr_jupyter::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
plaintext

any and all#

let id = "Iterator";
assert!( id.chars().any(char::is_uppercase));
assert!(!id.chars().all(char::is_uppercase));
rust

position, rposition, and ExactSizeIterator#

let text = “Xerxes”; assert_eq!(text.chars().position(|c| c == ‘e’), Some(1)); assert_eq!(text.chars().position(|c| c == ‘z’), None); let bytes = b”Xerxes”; assert_eq!(bytes.iter().rposition(|&c| c == b’e’), Some(4)); assert_eq!(bytes.iter().rposition(|&c| c == b’X’), Some(0));

fold and rfold#

let a = [5, 6, 7, 8, 9, 10];
assert_eq!(a.iter().fold(0, |n, _| n+1), 6); // count
assert_eq!(a.iter().fold(0, |n, i| n+i), 45); // sum
assert_eq!(a.iter().fold(1, |n, i| n*i), 151200); // produc
// max
assert_eq!(a.iter().cloned().fold(i32::min_value(), std::cmp::max),
10);
rust
let a = ["Pack", "my", "box", "with",
"five", "dozen", "liquor", "jugs"];
// See also: the `join` method on slices, which won't
// give you that extra space at the end.
let pangram = a.iter()
.fold(String::new(), |s, w| s + w + " ");
assert_eq!(pangram, "Pack my box with five dozen liquor jugs ");
rust
let weird_pangram = a.iter()
.rfold(String::new(), |s, w| s + w + " ");
assert_eq!(weird_pangram, "jugs liquor dozen five with box my Pack ");
rust

try_fold and try_rfold#

rust
The nth method takes an index n, skips that many items from the iterator, and
returns the next item, or None if the sequence ends before that point. Calling .nth(0)
is equivalent to .next()
plaintext

第nth方法采用索引n,从迭代器中跳过那么多项,并返回下一项,如果序列在该点之前结束,则返回无。调用. nth(0)等价于.next()
plaintext
let mut squares = (0..10).map(|i| i*i);
assert_eq!(squares.nth(4), Some(16));
assert_eq!(squares.nth(0), Some(25));
assert_eq!(squares.nth(6), None);
rust
The variable `squares` has type `std::iter::Map<std::ops::Range<i32>, impl Fn(i32) -> i32>` which cannot be persisted.
You might be able to fix this by creating a `Box<dyn YourType>`. e.g.
let v: Box<dyn core::fmt::Debug> = Box::new(foo());
Alternatively, you can prevent evcxr from attempting to persist
the variable by wrapping your code in braces.
plaintext

last#

let squares = (0..10).map(|i| i*i);
assert_eq!(squares.last(), Some(81));
rust
The variable `squares` has type `std::iter::Map<std::ops::Range<i32>, impl Fn(i32) -> i32>` which cannot be persisted.
You might be able to fix this by creating a `Box<dyn YourType>`. e.g.
let v: Box<dyn core::fmt::Debug> = Box::new(foo());
Alternatively, you can prevent evcxr from attempting to persist
the variable by wrapping your code in braces.
plaintext

find, rfind, and find_map#

assert_eq!(populations.iter().find(|&(_name, &pop)| pop > 1_000_000),
None);
assert_eq!(populations.iter().find(|&(_name, &pop)| pop > 500_000),
Some((&"Portland", &583_776)));
rust
let big_city_with_volcano_park = populations.iter()
.find_map(|(&city, _)| {
if let Some(park) = find_volcano_park(city, &parks) {
// find_map returns this value, so our caller knows
// *which* park we found.
return Some((city, park.name));
}
// Reject this item, and continue the search.
None
});
assert_eq!(big_city_with_volcano_park,
Some(("Portland", "Mt. Tabor Park")));
rust
[E0425] Error: cannot find function `find_volcano_park` in this scope
   ╭─[command_50:1:1]

 3 │ if let Some(park) = find_volcano_park(city, &parks) {
   │                     ────────┬────────
   │                             ╰────────── not found in this scope
───╯
plaintext

Building Collections: collect and FromIterator#

let args: Vec<String> = std::env::args().collect();
rust
use std::collections::{HashSet, BTreeSet, LinkedList, HashMap, BTreeMap};
let args: HashSet<String> = std::env::args().collect();
let args: BTreeSet<String> = std::env::args().collect();
let args: LinkedList<String> = std::env::args().collect();
// Collecting a map requires (key, value) pairs, so for this example,
// zip the sequence of strings with a sequence of integers.
let args: HashMap<String, usize> = std::env::args().zip(0..).collect();
let args: BTreeMap<String, usize> = std::env::args().zip(0..).collect();
rust

The Extend Trait#

let mut v: Vec<i32> = (0..5).map(|i| 1 << i).collect();
v.extend(&[31, 57, 99, 163]);
assert_eq!(v, &[1, 2, 4, 8, 16, 31, 57, 99, 163]);
rust

partition#

let things = ["doorknob", "mushroom", "noodle", "giraffe", "grapefruit"];
rust
// odd-numbered letter.
let (living, nonliving): (Vec<&str>, Vec<&str>)
= things.iter().partition(|name| name.as_bytes()[0] & 1 != 0);
assert_eq!(living, vec!["mushroom", "giraffe", "grapefruit"]);
assert_eq!(nonliving, vec!["doorknob", "noodle"]);
rust

for_each and try_for_each#

["doves", "hens", "birds"].iter()
.zip(["turtle", "french", "calling"].iter())
.zip(2..5)
.rev()
.map(|((item, kind), quantity)| {
format!("{} {} {}", quantity, kind, item)
})
.for_each(|gift| {
println!("You have received: {}", gift);
});
rust
You have received: 4 calling birds
You have received: 3 french hens
You have received: 2 turtle doves
plaintext
for gift in ["doves", "hens", "birds"].iter()
.zip(["turtle", "french", "calling"].iter())
.zip(2..5)
.rev()
.map(|((item, kind), quantity)| {
format!("{} {} {}", quantity, kind, item)
})
{
println!("You have received: {}", gift);
}
rust
You have received: 4 calling birds
You have received: 3 french hens
You have received: 2 turtle doves

()
plaintext
rust

Macro#

image.png

macro_rules! bad_assert_eq {
($left:expr, $right:expr) => ({
match ($left, $right) {
(left_val, right_val) => {
if !(left_val == right_val) {
panic!("assertion failed" /* ... */);
}
}
}
});
}
rust
let s= "a rose".to_string();
bad_assert_eq!(s,"a rose");
println!("{}",s)
rust
[E0382] Error: borrow of moved value: `s`
   ╭─[command_3:1:1]

 1 │ let s= "a rose".to_string();
   │     ┬
   │     ╰── move occurs because `s` has type `String`, which does not implement the `Copy` trait
 2 │ bad_assert_eq!(s,"a rose");
   │                ┬│
   │                ╰── value moved here
   │                 │
   │                 ╰─ help: consider cloning the value if the performance cost is acceptable: `.clone()`
 3 │ println!("{}",s)
   │               ┬
   │               ╰── value borrowed here after move
───╯
plaintext

image.png

println!(stringify!("hello"))
rust
"hello"

()
plaintext
println!(env!("USER"))
rust
realcpf

()
plaintext
const TEXT : &str = include_str!("/home/realcpf/Documents/rustRepos/rust-prog-2021/hello.txt");
println!("{}",TEXT);
rust
hello world
plaintext
use std::collections::HashMap;
#[derive(Clone, PartialEq, Debug)]
enum Json {
    Null,
    Boolean(bool),
    Number(f64),
    String(String),
    Array(Vec<Json>),
    Object(Box<HashMap<String, Json>>)
}
rust
let students = Json::Array(vec![
Json::Object(Box::new(vec![
("name".to_string(), Json::String("Jim Blandy".to_string())),
("class_of".to_string(), Json::Number(1926.0)),
("major".to_string(), Json::String("Tibetan throat singing".to_string()))
].into_iter().collect())),
Json::Object(Box::new(vec![
("name".to_string(), Json::String("Jason Orendorff".to_string())),
("class_of".to_string(), Json::Number(1702.0)),
("major".to_string(), Json::String("Knots".to_string()))
].into_iter().collect()))
]);
rust
macro_rules! json {
    (null) => {
        Json::Null
    };
    ([ $( $element:expr ),*]) => {
        Json:Array(vec![ $( $element ),*])
    };
}
rust
assert_eq!(json!(null),Json::Null)
rust
()
plaintext
let mm = json!(
    [
        {
            "a":3.33
        }
    ]
);
let hand_coded_value =
Json::Array(vec![
Json::Object(Box::new(vec![
("pitch".to_string(), Json::Number(440.0))
].into_iter().collect()))
]);
assert_eq!(mm,hand_coded_value)
rust
Error: expected one of `.`, `;`, `?`, `}`, or an operator, found `:`
   ╭─[command_15:1:1]

 4 │             "a":3.33
   │                ┬
   │                ╰── expected one of `.`, `;`, `?`, `}`, or an operator
───╯
Error: path separator must be a double colon
[unused_macros] Error: unused macro definition: `bad_assert_eq`
plaintext

image.png

macro_rules! json {
(null) => {
Json::Null
};
([ $( $element:tt ),* ]) => {
Json::Array(...)
};
({ $( $key:tt : $value:tt ),* }) => {
Json::Object(...)
};
($other:tt) => {
... // TODO: Return Number, String, or Boolean
};
    }
rust
macro_rules! impl_from_num_for_json {
    ( $( $t:ident )* ) => {
        $(
            impl From<$t> for Json {
                fn from(n: $t) -> Json {
                    Json::Number(n as f64)
                }
            }
        )*
    };
}
rust
impl_from_num_for_json!(u8 i8 u16 i16 u32 i32 u64 i64 u128 i128
usize isize f32 f64);
rust
let width = 4.0;
let desc =
json!({
"width": width,
"height": (width * 9.0 / 4.0)
});
rust
rust

Collections#

image.png

// Create an empty vector
let mut numbers: Vec<i32> = vec![];
// Create a vector with given contents
let words = vec!["step", "on", "no", "pets"];
let mut buffer = vec![0u8; 1024]; // 1024 zeroed-out bytes
rust

image.png

let lines=vec![];
let numbers = vec![];
// Get a reference to an element
let first_line = &lines[0];
// Get a copy of an element
let fifth_number = numbers[4]; // requires Copy
let second_line = lines[1].clone(); // requires Clone
// Get a reference to a slice
let my_ref = &buffer[4..12];
// Get a copy of a slice
let my_copy = buffer[4..12].to_vec(); // requires Clone
rust
[E0282] Error: type annotations needed for `&T`
   ╭─[command_5:1:1]

 4 │ let first_line = &lines[0];
   │     ─────┬────│
   │          ╰────── error: type annotations needed for `&T`
   │               │
   │               ╰─ help: consider giving `first_line` an explicit type, where the placeholders `_` are specified: `: &T`

 7 │ let second_line = lines[1].clone(); // requires Clone
   │                   ────┬───
   │                       ╰───── type must be known at this point
───╯
plaintext
let slice = [0, 1, 2, 3];
if let Some(item) = slice.first() {
    println!("we got {}",item);
}
assert_eq!(slice.get(2), Some(&2));
assert_eq!(slice.get(4), None);
rust
we got 0
plaintext
let mut slice = [0, 1, 2, 3];
{
    let last = slice.last_mut().unwrap();
    assert_eq!(*last,3);
    *last = 99;
}
assert_eq!(slice,[0,1,2,99]);
rust
let v = [1, 2, 3, 4, 5, 6, 7, 8, 9];
assert_eq!(v.to_vec(),
vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
assert_eq!(v[0..6].to_vec(),
vec![1, 2, 3, 4, 5, 6]);
rust
use std::collections::HashSet;
let mut byte_vec = b"Misssssssissippi".to_vec();
byte_vec.dedup();
assert_eq!(&byte_vec, b"Misisipi");
let mut byte_vec = b"Misssssssissippi".to_vec();
let mut seen = HashSet::new();
byte_vec.retain(|r| seen.insert(*r));
assert_eq!(&byte_vec, b"Misp");
rust
assert_eq!([[1, 2], [3, 4], [5, 6]].concat(),
vec![1, 2, 3, 4, 5, 6]);
assert_eq!([[1, 2], [3, 4], [5, 6]].join(&0),
vec![1, 2, 0, 3, 4, 0, 5, 6]);
rust
{
    let v = vec![0, 1, 2, 3];
    let i = 1;
    let j = 2;
    let a = &v[i];
    let b = &v[j];
    let mid = v.len() / 2;
    let front_half = &v[..mid];
    let back_half = &v[mid..];
}
rust
()
plaintext
{
    let mut v = vec![0, 1, 2, 3];
        let i = 1;
    let j = 2;
    let a = &mut v[i];
    let b = &mut v[j]; // error: cannot borrow `v` as mutable
    // more than once at a time
    *a = 6; // references `a` and `b` get used here,
    *b = 7; // so their lifetimes must overlap
}
rust
[E0499] Error: cannot borrow `v` as mutable more than once at a time
   ╭─[command_19:1:1]

 5 │     let a = &mut v[i];
   │                  ┬
   │                  ╰── first mutable borrow occurs here
 6 │     let b = &mut v[j]; // error: cannot borrow `v` as mutable
   │                  ┬
   │                  ╰── second mutable borrow occurs here

 8 │     *a = 6; // references `a` and `b` get used here,
   │     ───┬──
   │        ╰──── first borrow later used here
───╯
plaintext

image.png image.png

assert_eq!([1, 2, 3, 4].starts_with(&[1, 2]), true);
assert_eq!([1, 2, 3, 4].starts_with(&[2, 3]), false);
assert_eq!([1, 2, 3, 4].ends_with(&[3, 4]), true);
rust
use std::collections::VecDeque;
let v = VecDeque::from(vec![1, 2, 3, 4]);
rust
use std::collections::binary_heap::PeekMut;
use std::collections::BinaryHeap;
{

    let mut heap = BinaryHeap::from(vec![2, 3, 8, 6, 9, 5, 4]);
    if let Some(top) = heap.peek_mut() {
        if *top > 10 {
            PeekMut::pop(top);
        }
    }
}
rust
use std::collections::HashMap;
let mut vote_counts: HashMap<String, usize> = HashMap::new();
vote_counts.insert(String::from("a"),1);
let ballots = vec![String::from("a")];
for name in ballots {
    let count = vote_counts.entry(name).or_insert(0);
    *count += 1;
}
rust
()
plaintext
let s1 = "hello".to_string();
let s2 = "hello".to_string();
println!("{:p}", &s1 as &str); // 0x7f8b32060008
println!("{:p}", &s2 as &str); // 0x7f8b32060010
rust
0x55ae0bc7af70
0x55ae0bc7e270
plaintext
use std::hash::{Hash, Hasher, BuildHasher};
fn compute_hash<B, T>(builder: &B, value: &T) -> u64
where B: BuildHasher, T: Hash
{
let mut hasher = builder.build_hasher(); // 1. start the algorithm
value.hash(&mut hasher); // 2. feed it data
hasher.finish() // 3. finish, producing a u64
}
rust
rust

Concurrency#

image.png image.png

use std::thread;
thread::spawn(||{
    let curr = thread::current();
    let name = curr.name().unwrap_or("wrong").to_string();
    println!("hello in thread {}",name);
})
rust
JoinHandle { .. }
plaintext
use std::{fs, thread};
use std::sync::mpsc;
let (sender, receiver) = mpsc::channel();
let handle = thread::spawn(move || {
for filename in documents {
let text = fs::read_to_string(filename)?;
if sender.send(text).is_err() {
break;
}
}
Ok(())
});
rust
[E0425] Error: cannot find value `documents` in this scope
   ╭─[command_10:1:1]

 5 │ for filename in documents {
   │                 ────┬────
   │                     ╰────── not found in this scope
───╯
plaintext

Multiconsumer Channels Using Mutexes#

image.png

Read/Write Locks (RwLock)#

image.png

Condition Variables (Condvar)#

image.png

Atomics#

use std::sync::atomic::{AtomicIsize,Ordering};
let atom = AtomicIsize::new(0);
atom.fetch_add(1, Ordering::SeqCst);
rust

These methods may compile to specialized machine language instructions. On the x86-64 architecture, this .fetch_add() call compiles to a lock incq instruction, where an ordinary n += 1 might compile to a plain incq instruction or any number of variations on that theme. The Rust compiler also has to forgo some optimizations around the atomic operation, since—unlike a normal load or store—it can legiti‐ mately affect or be affected by other threads right away 这些方法可以编译成专门的机器语言指令。在x86-64架构上,this.fetch_add()调用编译成锁incq指令,其中普通的n+=1可能编译成普通的incq指令或该主题的任何数量的变体。Rust编译器还必须放弃围绕原子操作的一些优化,因为与正常的加载或存储不同,它可以合法地立即影响或受到其他线程的影响

use std::sync::Arc;
use std::sync::atomic::AtomicBool;
let cancel_flag = Arc::new(AtomicBool::new(false));
let worker_cancel_flag = cancel_flag.clone();
rust
rust

Traits and Generics#

[A] computer scientist tends to be able to deal with nonuniform structures—case 1, case 2, case 3—while a mathematician will tend to want one unifying axiom that governs an entire system. —Donald Knuth

use std::io::Write;
fn say_hello(out: &mut dyn Write) -> std::io::Result<()> {
    out.write_all(b"hello world\n")?;
    out.flush()
}
rust
use std::fs::File;
let mut local_file = File::create("hello.txt")?;
say_hello(&mut local_file)?;
rust
let mut bytes = vec![];
say_hello(&mut bytes)?;
assert_eq!(bytes,b"hello world\n");
rust
fn min<T: Ord>(v1: T,v2: T) -> T {
    if v1 <= v2 {
        v1
    } else {
        v2
    }
}
rust

image.png

Trait Objects#

use std::io::Write;
let mut buf: Vec<u8> = vec![];
let writer: dyn Write = buf;
rust
{
    let mut buf: Vec<u8> =  vec![];
    let writer: &mut dyn Write = &mut buf;
}
rust
()
plaintext

内存布局#

image.png

Generic Functions and Type Parameters#

let v1 = (0..100).collect();
rust
[E0282] Error: type annotations needed
   ╭─[command_11:1:1]

 1 │ let v1 = (0..100).collect();
   │     ─┬│
   │      ╰── error: type annotations needed
   │       │
   │       ╰─ help: consider giving `v1` an explicit type: `: Vec<_>`
───╯
plaintext
let v2 = (0..100).collect::<Vec<i32>>();
rust

image.png

Defining and Implementing Traits#

image.png image.png image.png

Traits and Other People’s Types#

trait IsEmoji {
    fn is_emoji(&self) -> bool;
}
impl IsEmoji for char {
    fn is_emoji(&self) -> bool {
        false
    }
}
assert_eq!('$'.is_emoji(), false);
rust

Subtraits#

image.png

fn dot(v1: &[i64], v2: &[i64]) -> i64 {
let mut total = 0;
for i in 0 .. v1.len() {
total = total + v1[i] * v2[i];
}
total
}
rust
fn dot<N>(v1: &[N], v2: &[N]) -> N {
let mut total: N = 0;
for i in 0 .. v1.len() {
total = total + v1[i] * v2[i];
}
total
}
rust
use std::ops::{Add, Mul};
fn dot<N: Add + Mul + Default>(v1: &[N], v2: &[N]) -> N {
let mut total = N::default();
for i in 0 .. v1.len() {
total = total + v1[i] * v2[i];
}
total
}
rust
use std::ops::{Add, Mul};
fn dot<N>(v1: &[N], v2: &[N]) -> N
where N: Add<Output=N> + Mul<Output=N> + Default + Copy
{
let mut total = N::default();
for i in 0 .. v1.len() {
total = total + v1[i] * v2[i];
}
total
}
rust
rust

Operator Overloading#

Operator Overloading#

image.png

use std::ops::Add;
assert_eq!(4.125f32.add(5.75), 9.875);
assert_eq!(10.add(20), 10 + 20);
rust

use std::ops::Add;
impl<T> Add for Complex<T>
where
T: Add<Output = T>,
{
type Output = Self;
fn add(self, rhs: Self) -> Self {
Complex {
re: self.re + rhs.re,
im: self.im + rhs.im,
}
}
}
rust

image.png image.png image.png

let s = "d\x6fv\x65t\x61i\x6c".to_string();
let t = "\x64o\x76e\x74a\x69l".to_string();
assert!(s == t); // s and t are only borrowed...
rust
// ... so they still have their values here.
assert_eq!(format!("{} {}", s, t), "dovetail dovetail");
rust
assert!("ungula" != "ungulate");
assert!("ungula".ne("ungulate"));
rust
assert!(f64::is_nan(0.0 / 0.0));
assert_eq!(0.0 / 0.0 == 0.0 / 0.0, false);
assert_eq!(0.0 / 0.0 != 0.0 / 0.0, true);
rust
assert_eq!(0.0 / 0.0 < 0.0 / 0.0, false);
assert_eq!(0.0 / 0.0 > 0.0 / 0.0, false);
assert_eq!(0.0 / 0.0 <= 0.0 / 0.0, false);
assert_eq!(0.0 / 0.0 >= 0.0 / 0.0, false);
rust

image.png

#[derive(Debug, PartialEq)]
struct Interval<T> {
lower: T, // inclusive
upper: T, // exclusive
}
rust
use std::cmp::{Ordering, PartialOrd}
impl <T: PartialOrd> PartialOrd<Interval<T>> for Interval<T> {
    fn partial_cmp(&self, other: &Interval<T>) -> Option<Ordering> {
        if self == other {
            Some(Ordering::Equal)
        } else if self.lower >= other.upper {
            Some(Ordering::Greater)
        } else if self.upper <= other.lower {
            Some(Ordering::Less)
        } else {
            None
        }
    }
}
rust
assert!(Interval { lower: 10, upper: 20 } < Interval { lower: 20, upper: 40 });
assert!(Interval { lower: 7, upper: 8 } >= Interval { lower: 0, upper: 1 });
assert!(Interval { lower: 7, upper: 8 } <= Interval { lower: 7, upper: 8 });
// Overlapping intervals aren't ordered with respect to each other.
let left = Interval { lower: 10, upper: 30 };
let right = Interval { lower: 20, upper: 40 };
assert!(!(left < right));
assert!(!(left >= right));
rust

Index and IndexMut#

use std::collections::HashMap;
let mut m = HashMap::new();
m.insert("十", 10);
m.insert("百", 100);
m.insert("千", 1000);
m.insert("万", 1_0000);
m.insert("億", 1_0000_0000);
assert_eq!(m["十"], 10);
assert_eq!(m["千"], 1000);
rust
use std::ops::Index;
assert_eq!(*m.index("十"), 10);
assert_eq!(*m.index("千"), 1000);
rust
let mut desserts =
vec!["Howalon".to_string(), "Soan papdi".to_string()];
desserts[0].push_str(" (fictional)");
desserts[1].push_str(" (real)");
use std::ops::IndexMut;
(*desserts.index_mut(0)).push_str(" (fictional)");
(*desserts.index_mut(1)).push_str(" (real)");
rust
struct Image<P> {
    width: usize,
    pixels: Vec<P>,
}
rust
impl<P: Default + Copy> Image<P> {
    fn new(width: usize, height:usize) -> Image<P> {
        Image { width: width, pixels: vec![P::default();width * height], }
    }
}
rust
impl<P> std::ops::Index<usize> for Image<P> {
    type Output = [P];
    fn index(&self, index: usize) -> &Self::Output {
        let start = index * self.width;
        &self.pixels[start .. start + self.width]
    }
}
rust
impl<P> std::ops::IndexMut<usize> for Image<P> {
fn index_mut(&mut self, row: usize) -> &mut [P] {
let start = row * self.width;
&mut self.pixels[start..start + self.width]
}
}
rust
rust

Science is nothing else than the search to discover unity in the wild variety of nature—or, more exactly, in the variety of our experience. Poetry, painting, the arts are the same search, in Coleridge’s phrase, for unity in variety. —Jacob Bronowski image.png

Drop#

struct Appellation {
name: String,
nicknames: Vec<String>
}
rust
impl Drop for Appellation {
    fn drop(&mut self) {
        println!("Droping {}",&self.name);
        if !&self.nicknames.is_empty() {
            println!("AKA {}",&self.nicknames.join(","));
        }
        println!("");
    }
}
rust
{
let mut a = Appellation {
name: "Zeus".to_string(),
nicknames: vec!["cloud collector".to_string(),
"king of the gods".to_string()]
};
println!("before assignment");
a = Appellation { name: "Hera".to_string(), nicknames: vec![] };
println!("at end of block");
}
rust
before assignment
Droping Zeus
AKA cloud collector,king of the gods

at end of block
Droping Hera



()
plaintext

Sized#

image.png

struct RcBox<T: ?Sized> {
ref_count: usize,
value: T,
}
rust
use std::fmt::Display;
fn display(boxed: &RcBox<dyn Display>) {
println!("For your enjoyment: {}", &boxed.value);
}
rust
{
    let boxed_lunch: RcBox<String> = RcBox {
ref_count: 1,
value: "lunch".to_string()
};
use std::fmt::Display;
let boxed_displayable: &RcBox<dyn Display> = &boxed_lunch;
    display(&boxed_lunch);
}
rust
For your enjoyment: lunch


()
plaintext

Clone#

image.png

Copy#

image.png

Deref and DerefMut#

struct Selector<T> {
/// Elements available in this `Selector`.
elements: Vec<T>,
/// The index of the "current" element in `elements`. A `Selector`
/// behaves like a pointer to the current element.
current: usize
}
rust
use std::ops::{Deref, DerefMut};
impl<T> Deref for Selector<T> {
type Target = T;
fn deref(&self) -> &T {
&self.elements[self.current]
}
}
rust
impl<T> DerefMut for Selector<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.elements[self.current]
    }
}
rust
[E0119] Error: conflicting implementations of trait `DerefMut` for type `Selector<_>`
   ╭─[command_17:1:1]

 1 │ impl<T> DerefMut for Selector<T> {
   │ ────────────────┬───────────────
   │                 ╰───────────────── conflicting implementation for `Selector<_>`
───╯
plaintext
let mut s = Selector { elements: vec!['x', 'y', 'z'],
current: 2 };
// Because `Selector` implements `Deref`, we can use the `*` operator to
// refer to its current element.
assert_eq!(*s, 'z');
rust
// Assert that 'z' is alphabetic, using a method of `char` directly on a
// `Selector`, via deref coercion.
assert!(s.is_alphabetic());
// Change the 'z' to a 'w', by assigning to the `Selector`'s referent.
*s = 'w';
assert_eq!(s.elements, ['x', 'y', 'w']);
rust
let s = Selector { elements: vec!["good", "bad", "ugly"],
current: 2 };
fn show_it(thing: &str) { println!("{}", thing); }
show_it(&s);
rust
ugly
plaintext
use std::fmt::Display;
fn show_it_generic<T: Display>(thing: T) { println!("{}", thing); }
show_it_generic(&s);
rust
[E0277] Error: `Selector<&str>` doesn't implement `std::fmt::Display`
   ╭─[command_22:1:1]

 2 │ fn show_it_generic<T: Display>(thing: T) { println!("{}", thing); }
   │                       ───┬───
   │                          ╰───── required by this bound in `show_it_generic`
 3 │ show_it_generic(&s);
   │ ───────┬───────  ┬
   │        ╰──────────── required by a bound introduced by this call
   │                  │
   │                  ╰── `Selector<&str>` cannot be formatted with the default formatter
   │                  │
   │                  ╰── help: consider dereferencing here: `*`
───╯
plaintext
show_it(&*s)
rust
ugly


()
plaintext
show_it(&s as &str)
rust
ugly


()
plaintext

Default#

use std::collections::HashSet;
let squares = [4, 9, 16, 25, 36, 49, 64];
let (powers_of_two, impure): (HashSet<i32>, HashSet<i32>)
= squares.iter().partition(|&n| n & (n-1) == 0);
assert_eq!(powers_of_two.len(), 3);
assert_eq!(impure.len(), 4);
rust
let (upper, lower): (String, String)
= "Great Teacher Onizuka".chars().partition(|&c| c.is_uppercase());
assert_eq!(upper, "GTO");
assert_eq!(lower, "reat eacher nizuka");
rust

AsRef and AsMut#

/*
What open really wants is a &Path, the type representing a filesystem path. But with
this signature, open accepts anything it can borrow a &Path from—that is, anything
that implements AsRef<Path>. Such types include String and str, the operating sys‐
tem interface string types OsString and OsStr, and of course PathBuf and Path; see
the library documentation for the full list. This is what allows you to pass string liter‐
als to open:
*/
let dot_vim = std::fs::File::open("/home/realcpf/.vim");
rust

Borrow and BorrowMut#

From and Into#

use std::net::Ipv4Addr;
fn ping<A>(address: A) -> std::io::Result<bool>
    where A: Into<Ipv4Addr>{
        let ipv4_address = address.into();
        std::io::Result::Ok(true)
}
rust
println!("{:?}", ping(Ipv4Addr::new(23, 21, 68, 141))); // pass an Ipv4Addr
println!("{:?}", ping([66, 146, 219, 98])); // pass a [u8; 4]
println!("{:?}", ping(0xd076eb94_u32)); // pass a u32
rust
Ok(true)
Ok(true)
Ok(true)
plaintext
let addr1 = Ipv4Addr::from([66, 146, 219, 98]);
let addr2 = Ipv4Addr::from(0xd076eb94_u32);
rust
let text = "hello world".to_string();
let bytes: Vec<u8> = text.into();
rust

TryFrom and TryInto#

let huge = 2_000_000_000_000i64;
let smaller = huge as i32;
println!("{}", smaller); // -1454759936
rust
-1454759936
plaintext
use std::convert::TryInto;
let smaller: i32 = huge.try_into().unwrap_or(i32::MAX);
rust
let smaller: i32 = huge.try_into().unwrap_or_else(|_| {
    if huge >= 0 {
        i32::MAX
    } else {
        i32::MIN
    }
});
rust
rust
rust
Programming Rust 2nd Edition-2021
https://realcpf.tech/blog/programming-rust-2nd-edition-2021
Author 刘佳成
Published at 2024年2月29日