Nắm vững các kỹ năng về quản lý gói Cargo và cú pháp cơ bản giúp chúng ta có thể tự tin để có thể build build bất kỳ một project nào. Bài viết vẫn còn cần bổ sung và đi sâu một số mục như làm việc với workspace. Điều này sẽ được làm trong những ngày tới.

Outline

  • Chương 0: Cargo căn bản?
    • Tạo một project
    • Build project
    • Chạy Project mới
    • Chạy Project có sẵn
    • Dependencies
    • Package layout
    • Cargo.toml và Cargo.lock
    • Cargo Home
    • Cargo Test
    • CI/CD
    • Build Cache
  • Chương 1: Cargo Modules
    • Thuật ngữ
    • Module
    • Path
  • Chương 2: Cargo reference
    • Dependencies từ crates.io
    • Overriding sự phụ thuộc
    • Định dạng Manifest Format
    • Features
    • Workspace
    • Profiles
    • Configuration
    • Biến môi trường
    • Build Script
    • Format trong rust
    • Sử dụng Clippy
    • Toolchain trong rust
    • Report timing
    • Phân giải phụ thuộc
  • Chương 3: Cargo command Tip & Tricks
    • Tip & Tricks
  • Chương 4: Một tour ngôn ngữ
    • Kiểu dữ liệu nguyên thuỷ(Primitive types)
    • Biến và biến mutability
    • Hàm
    • Closures
    • Strings
    • Điều kiện rẽ nhánh
    • Câu lệnh Match
    • Loops
    • Kiểu tự định nghĩa
    • Impl trong struct
    • Impl trong enum
    • Modules, imports, use keyword
    • Collections
  • Chương 5: Bài tập về nhà

References:

  • https://doc.rust-lang.org/cargo/
  • https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html
  • https://semver.org/
  • https://toml.io/en/
  • https://doc.rust-lang.org/rustdoc/write-documentation/re-exports.html

Chương 0: Cargo là gì?

Là một công cụ quản lý các gói trong Rust. Nó làm 4 thứ:

Tạo một project

cargo new <name> --bin

Nó sẽ gồm một file Cargo.toml và src/main.rs. Cargo.toml đươc gọi là Manifest

$ tree .
.
├── Cargo.toml
└── src
    └── main.rs

Build project

cargo build

Chạy Project mới

cargo run
cargo run --release

Chạy project có sẵn

Chỉ cần cargo build

 git clone https://github.com/rust-lang/regex.git
$ cd regex
cargo build

Dependencies

[dependencies]
time = "0.1.12"

Cần để ý các SemVer ở đây [https://semver.org/]

Ví dụ:

[package]
name = "hello_world"
version = "0.1.0"
edition = "2021"

[dependencies]
time = "0.1.12"
regex = "0.1.41"

Package layout

.
├── Cargo.lock
├── Cargo.toml
├── src/
│   ├── lib.rs
│   ├── main.rs
│   └── bin/
│       ├── named-executable.rs
│       ├── another-executable.rs
│       └── multi-file-executable/
│           ├── main.rs
│           └── some_module.rs
├── benches/
│   ├── large-input.rs
│   └── multi-file-bench/
│       ├── main.rs
│       └── bench_module.rs
├── examples/
│   ├── simple.rs
│   └── multi-file-example/
│       ├── main.rs
│       └── ex_module.rs
└── tests/
    ├── some-integration-tests.rs
    └── multi-file-test/
        ├── main.rs
        └── test_module.rs
  • Cargo.tomlCargo.lock nằm tại package root
  • Thư viện mặc định src/lib.rs
  • File executable mặc định là src/main.rs
  • Các ví dụ examples directory
  • Các files tests trong tests directory
  • Các file bếch dirrec

Bài tập: Tạo một hàm fibonaci và sử chạy lệnh cargo benches

Cargo.toml và Cargo.lock

Cargo Home

  • Định vị tại $HOME/.cargo/

  • Khi download thư viện thì Car

  • Một số biến môi trường có thể xem tại đây CARGO_HOME, CARGO_LOG RUSTFMT, CARGO_TARGET_DIR CARGO Link

  • Có thể cấu hình các biên môi trường này tại config.toml Link Ví dụ ta có thể tạo tại ~/.cargo/config.toml biến CARGO_TARGET_DIR

    [build]
    target-dir = "/path/to/your/target"
    
    Hoc có thể thiết lập biến môi trường export CARGO_TARGET_DIR=/path/to/your/target
    
  • Cấu hình credentials.toml để cung cấp cargo login

  • Thư mục bin, git, registry

Cargo Test

cargo test
cargo test foo
  • Ta có thể chạy trong 2 nơi là: Mỗi file trong src/tests/

CI/CD

Build Cache

  • target/debug/: Cho debug
  • target/release/: Cho release
  • target/foo: Cho profile foo. Ví dụ: cargo build --profile=foo và nội dụng file cần có như sau
[profile.foo]
inherits = "release"
opt-level = 1           # Use slightly better optimizations.
overflow-checks = false
strip = "debuginfo"

Note: Cần phải có inherits. Không thì sẽ không build được, trừ cho devrelease

  • Sử dụng --target cho

Chương 1: Cargo Modules

Trong chương này học về:

  • Packages: A Cargo feature that lets you build, test, and share crates
  • Crates: A tree of modules that produces a library or executable
  • Modules and use: Let you control the organization, scope, and privacy of paths
  • Paths: A way of naming an item, such as a struct, function, or module
  • Tip và tricks

Thuật ngữ

  • Crate: Phần nhỏ nhất của code mà Rust compiler xem xét tại một thời điểm. Nó có thể đến tử 2 form: binary crateslibrary crates. Khi nói đến crate thường hay nghĩ đến đó là thư viện

  • package: Là tập hợp 1 hoặc nhiều crates mà cung cấp một tập hợp các chức năng. Một package sẽ bao gồm một Cargo.toml file miêu tả cách build các crates

    • Một package có thể gồm chỉ một trong 2 files src/main.rsvà mộtsrc/lib.rs hoặc cả 2
    • Một package có thể gồm nhiều binary crates và định vị trong src/bin directory
  • modules: Nó là path cho phép bạn đặt tên cho mỗi items

Module

  • Nó là path cho phép bạn đặt tên cho mỗi items
  • Hữu dụng khi nhóm các code liên quan thành một chỗ
  • Là một phần của module system
  • Bắt đầu từ crate root: Nới chứa file src/lib.rs hoặc src/main.rs
  • Declaring modules: Eg: mod foo;. Có thể có 3 khả năng src/foo.rs hoặc src/foo/mod.rs hoặc trong lib.rs chứa mod foo{}
  • Declaring submodules: Chúng ta có thể submodule bên trong nới chưa file Cargo.toml bên trong
  • Path đến module crate::foo::func1. Path có thể là absolute path hoặc relative path
  • Có thể có privatepublic: Sử dụng từ khoá pub
  • Sử dụng từ khoá use để sử dụng Link

Path

  • absolute path: Như sử dụng crate từ khoá
  • relative path: Bắt đầu tự module hiện tại như self, super
mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}
    }
}

pub fn eat_at_restaurant() {
    // Absolute path
    crate::front_of_house::hosting::add_to_waitlist();

    // Relative path
    front_of_house::hosting::add_to_waitlist();
}
  • Có thể export sử dụng từ khoá pub trong pub fn add_to_waitlist() {}
mod front_of_house {
    pub mod hosting {
       pub fn add_to_waitlist() {}
    }
}

pub fn eat_at_restaurant() {
    // Absolute path
    crate::front_of_house::hosting::add_to_waitlist();

    // Relative path
    front_of_house::hosting::add_to_waitlist();
}
  • Sử dụng super

super chính là module cha của mod back_of_house

fn deliver_order() {}

mod back_of_house {
    fn fix_incorrect_order() {
        cook_order();
        super::deliver_order();
    }

    fn cook_order() {}
}
  • File chính là tên module mod foo thì có thể là foo.rs

  • Thư mục cũng có thể là tên module Nhưng phải chứa mod.rs bên trong

  • Sử dụnguse để rexport hoặc pub use

Ví dụ sau đây sẽ lỗi

mod front_of_house {
   pub mod hosting {
       pub fn add_to_waitlist() {}
   }
}

use crate::front_of_house::hosting;

mod customer {
   pub fn eat_at_restaurant() {
       hosting::add_to_waitlist();
   }
}

Có thể fix như sau:

mod front_of_house {
   pub mod hosting {
       pub fn add_to_waitlist() {}
   }
}

use crate::front_of_house::hosting;

mod customer {
   pub fn eat_at_restaurant() {
       super::hosting::add_to_waitlist();
   }
}

Sau đây là best practice. Khai báo use crate::front_of_house::hosting::add_to_waitlist;

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

use crate::front_of_house::hosting::add_to_waitlist;

pub fn eat_at_restaurant() {
    add_to_waitlist();
}
  • Nên chia thành các file riêng rẽ hoặc thư mục riêng rẽ
  • Một số cú pháp khác
use std::collections::*;
use std::io::{self, Write};
use rand::Rng;

Chương 2: Cargo reference

Dependencies từ crates.io

[dependencies]
time = "0.1.12"

Một số chú ý:

1.2.3  :=  >=1.2.3, <2.0.0
1.2    :=  >=1.2.0, <2.0.0
1      :=  >=1.0.0, <2.0.0
0.2.3  :=  >=0.2.3, <0.3.0
0.2    :=  >=0.2.0, <0.3.0
0.0.3  :=  >=0.0.3, <0.0.4
0.0    :=  >=0.0.0, <0.1.0
0      :=  >=0.0.0, <1.0.0

Overriding sự phụ thuộc

Trong một số case:

  • Đang testing một bugfix
  • Làm việc với minor không được public
  • Override lại resposity URL: Giốn với replace trong go
  • Sử dụng [patch] với nhiều versions

Sử du

Bài tập: Nghiên cứu [path] [replace] Link

Định dạng Manifest Format

Tham khảo tại Link

  • Sử dụng [build-dependencies] khi làm việc với các prebuild như build.rs
  • Sử dụng [[bin]] để build nhiều file binary crates. Eg:
# Example of customizing binaries in Cargo.toml.
[[bin]]
name = "cool-tool"
test = false
bench = false

[[bin]]
name = "frobnicator"
required-features = ["frobnicate"]
  • Sử dụng [[example]] để mô tả các ẽample
[[example]]
name = "foo"
crate-type = ["staticlib"]
  • Sử dụng [lib] để mô tả thư viện sẽ có một số trường đặc biệt proc-macro hoặc crate-type (bin, lib, rlib, dylib, cdylib, staticlib, and proc-macro)
[lib]
name = "foo"           # The name of the target.
path = "src/lib.rs"    # The source file of the target.
test = true            # Is tested by default.
doctest = true         # Documentation examples are tested by default.
bench = true           # Is benchmarked by default.
doc = true             # Is documented by default.
plugin = false         # Used as a compiler plugin (deprecated).
proc-macro = false     # Set to `true` for a proc-macro library.
harness = true         # Use libtest harness.
edition = "2015"       # The edition of the target.
crate-type = ["lib"]   # The crate types to generate.
required-features = [] # Features required to build this target (N/A for lib).
  • Sử dụng features Sử dụng default để thiết lập cờ mặc định . Cái này có thể bị thay đổi từ 2 cái --no-default-features trong command line hoặc trong default-features = false của mô tả [depencies]
[features]
default = ["ico", "webp"]
bmp = []
png = []
ico = ["bmp", "png"]
webp = []
  • Sử dụng [depencencies]

Bài tập: Tìm hiểu về việc cấu hình các thư viện, các features, phụ thuộc features

Features

  • Phụ thuộc features: sử dụng từ optional = true
[dependencies]
serde = { version = "1.0.133", optional = true }
rgb = { version = "0.8.25", optional = true }

[features]
serde = ["dep:serde", "rgb?/serde"]
  • Nếu sử dụng feature serde thì phụ thuộc serde trong crates.io và rgb crate cũng cho phép feature serde.
  • Trong trường hợp rgb không cho phép serde thì nó sẽ không được sử dụng
  • Command
cargo build -p foo -p bar --features foo-feat,bar-feat

Tip: Sử dụng cargo --list và cài đặt cargo install whatfeatures Sử dụng: cargo whatfeatures rand Sử dụng cargo feature serde để xem tất cả các feature của serde

bichkhe@newton:~/500gb/rust-projects/cargo-tour$ cargo feature rand
   Avaliable features for `rand`
default = ["std", "std_rng"]
alloc = ["rand_core/alloc"]
getrandom = ["rand_core/getrandom"]
min_const_gen = []
nightly = []
serde1 = ["serde", "rand_core/serde1"]
simd_support = ["packed_simd"]
small_rng = []
std = ["rand_core/std", "rand_chacha/std", "alloc", "getrandom", "libc"]
std_rng = ["rand_chacha"]
libc (optional)
log (optional)
packed_simd (optional)
rand_chacha (optional)
serde (optional)
bichkhe@newton:~/500gb/rust-projects/cargo-tour$ cargo feature serde
   Avaliable features for `serde`
default = ["std"]
alloc = []
derive = ["serde_derive"]
rc = []
std = []
unstable = []
serde_derive (optional)

Workspace

Bài tập: Khám phá workspace package

Profiles

  • Có 4 profiles dev, release, testbench

  • opt-level:

0: no optimizations
1: basic optimizations
2: some optimizations
3: all optimizations
"s": optimize for binary size
"z": optimize for binary size, but also turn off loop vectorization.
  • strip: vứt bỏ symbold và debuginfo từ file binary --> Nhẹ hơn
  • lto: link time optimize. Eg: false: thin LTO, true or fat: tối ưu qua tất cả crates
[profile.dev]
opt-level = 0
debug = true
split-debuginfo = '...'  # Platform-specific.
strip = "none"
debug-assertions = true
overflow-checks = true
lto = false
panic = 'unwind'
incremental = true
codegen-units = 256
rpath = false

Configuration

Tham khảo Link

/projects/foo/bar/baz/.cargo/config.toml
/projects/foo/bar/.cargo/config.toml
/projects/foo/.cargo/config.toml
/projects/.cargo/config.toml
/.cargo/config.toml
$CARGO_HOME/config.toml which defaults to:
Windows: %USERPROFILE%\.cargo\config.toml
Unix: $HOME/.cargo/config.toml
  • Một số lệnh config
# Most shells will require escaping.
cargo --config http.proxy=\"http://example.com\" …
# Spaces may be used.
cargo --config "net.git-fetch-with-cli = true"# TOML array example. Single quotes make it easier to read and write.
cargo --config 'build.rustdocflags = ["--html-in-header", "header.html"]'# Example of a complex TOML key.
cargo --config "target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))'.runner = 'my-runner'"# Example of overriding a profile setting.
cargo --config profile.dev.package.image.opt-level=3 …

[alias]     # command aliases
b = "build"
c = "check"
t = "test"
r = "run"
rr = "run --release"
recursive_example = "rr --example recursions"
space_example = ["run", "--release", "--", "\"command list\""]

Biến môi trường

Tìm hiểu tại Link

Cách kiểm tra

let version = env!("CARGO_PKG_VERSION");

Build Script

Thỉnh thoảng chúng ta có cần build một số file trước khi tiến hành chạy một build một chương trình rust. Vi dụ như việc sinh ra file .rs từ file .proto

// Example custom build script.
fn main() {
    // Tell Cargo that if the given file changes, to rerun this build script.
    println!("cargo::rerun-if-changed=src/hello.c");
    // Use the `cc` crate to build a C file and statically link it.
    cc::Build::new()
        .file("src/hello.c")
        .compile("hello");
}

Bài tập: Phân tích Near core https://github.com/near/nearcore Tìm hiểu file https://github.com/near/nearcore/blob/master/neard/build.rs

Format trong rust

Tạo file rustfmt.toml trong crate root

use_small_heuristics = "Max"
reorder_imports = true
edition = "2021"
# This option will merge imports, however it's only available in +nightly.
# imports_granularity = "Module"
# fn_args_density = "Compressed"
# overflow_delimited_expr = "true"

Sử dụng Clippy

Trong file .clippy.toml

too-many-arguments-threshold = 9

Toolchain trong rust

Tạo file rust-toolchain.toml trong crate root

[toolchain]
# This specifies the version of Rust we use to build.
# Individual crates in the workspace may support a lower version, as indicated by `rust-version` field in each crate's `Cargo.toml`.
# The version specified below, should be at least as high as the maximum `rust-version` within the workspace.
channel = "1.79.0"
components = ["rustfmt", "clippy", "rust-analyzer"]
targets = ["wasm32-unknown-unknown"]

Report timing

cargo build --timings

Publishing on crates.io

cargo login

image

Một file tại ~/.cargo/crendentials.toml

[registry]
token = "xxxx"

Phân giải phụ thuộc

Bài tập: https://doc.rust-lang.org/cargo/reference/resolver.html

Chương 3: Cargo command Tip & Tricks

Tip & Tricks

Link

  • Sử dụng cargo help khi bạn không biết làm gì
cargo help
  • Sử dụng cargo install để cài đặt một gọi tin tiện ích cargo. Nó sẽ được cài đặt trong cargo module
  • Sử dụng cargo --list để nhìn thấy các tiện ích được cài bởi cargo install
  • Sử dụng cargo init,cargo new để tìm mộ
  • Sử dụng cargo add để thêm một crate. Trong trường hợp, thêm feature sử dụng cargo add serde -F derive
  • Sử dụng cargo feature để nhìn thấy các features trong một thư viện. Eg: cargo feature serde
  • Sử dụng cargo whatfeatures để nhìn thấy các features hiện tại đang cài
  • Sử dụng cargo clean để làm nhẹ projects
  • Sử dụng cargo doc tạo tại liệu
  • Sử dụng cargo update trong một số trường hợp cài đặt các crates. EG: cargo update foo -p 1.2.4. Một số thư viện trong blockhain cũ, chúng ta có thể cần phải chỉ rõ chính xác version
  • Sử dụng cargo tree để xem phụ thuộc nhưng sẽ khá dài nếu project có nhiều crates
  • Nếu có 1 file Makefile.toml sử dụng --manifest-path
  • Một số trường hợp có thể dùng rustc để build Link

Chương 4: Một tour về ngôn ngữ

Kiểu dữ liệu nguyên thuỷ(Primitive types)

  • i8, i16, i32, i64, i128 và không dấu u8, u16, u32, u64, u128
  • isizeusize phụ thuộc vào hệ điều hành
  • f32, f64: kiểu số thực 32-bit và 64 bit
  • [T; N]: Mảng kiểu T có N phần tử
  • [T] mảng động kiểu T
  • str: String slices sử dụng như tham chiếu
  • (T, U, ..): Chuối vô hãn
  • fn(i32) -> i32

Biến và biến mutability

fn main() {
    let target = "world";
    let mut greeting = "Hello";
    println!("{}, {}", greeting, target);
    greeting = "How are you doing";
    target = "mate";
    println!("{}, {}", greeting, target);
}

Hàm

fn add(a: u64, b: u64) -> u64 {
    a + b
}
fn main() {
    let a: u64 = 17;
    let b = 3;
    let result = add(a, b);
    println!("Result {}", result);
}

Hoặc

fn increase_by(mut val: u32, how_much: u32) {
    val += how_much;
    println!("You made {} points", val);
}
fn main() {
    let score = 2048;
    increase_by(score, 30);
}

Closures

fn main() {
       let doubler = |x| x * 2;
       let value = 5;
       let twice = doubler(value);
       println!("{} doubled is {}", value, twice);
       let big_closure = |b, c| {
           let z = b + c;
z * twice };
       let some_number = big_closure(1, 2);
       println!("Result from closure: {}", some_number);
   }

Strings

fn main() {
    let question = "How are you ?";
    let person: String = "Bob".to_string();
    let namaste = String::from("नमते");
    // a &str type
    // unicodes yay!
    println!("{}! {} {}", namaste, question, person);
}

Điều kiện rẽ nhánh

  • if else
 fn main() {
       let rust_is_awesome = true;
       if rust_is_awesome {
           println!("Indeed");
       } else {
           println!("Well, you should try Rust !");
       }
}
  • Gán lại giá trị
  fn main() {
       let result = if 1 == 2 {
           "Wait, what ?"
       } else {
           "Rust makes sense"
       };
       println!("You know what ? {}.", result);
   }

Câu lệnh Match

fn req_status() -> u32 {
       200
}
   fn main() {
       let status = req_status();
       match status {
} }

Loops

// loops.rs
fn main() {
       let mut x = 1024;
       loop {
}

Hoặc

fn silly_sub(a: i32, b: i32) -> i32 {
    let mut result = 0;
    'increment: loop {
        if result == a {
            let mut dec = b;
            'decrement: loop {
                if dec == 0 {
                    // breaks directly out of 'increment loop
                    break 'increment;
                } else {
                    result -= 1;
                    dec -= 1;
                }
            }
        } else {
            result += 1;
        }
    }
    result
}
fn main() {
    let a = 10;
    let b = 4;
    let result = silly_sub(a, b);
    println!("{} minus {} is {}", a, b, result);
}
  • while
fn main() {
    let mut x = 1000;
    while x > 0 {
        println!("{} more runs to go", x);
        x -= 1;
    }
}
  • for
fn main() {
    // does not include 10
    print!("Normal ranges: ");
    for i in 0..10 {
        print!("{},", i);
    }
    println!(); // just a newline
    print!("Inclusive ranges: ");
    // counts till 10
    for i in 0..=10 {
        print!("{},", i);
    }
}

Kiểu tự định nghĩa

  • Struct
struct Dummy;
fn main() {
    let value = Dummy;
}
  • Tupple
struct Color(u8, u8, u8);
fn main() {
    let white = Color(255, 255, 255);
    // You can pull them out by index
    let red = white.0;
    let green = white.1;
    let blue = white.2;
    println!("Red value: {}", red);
    println!("Green value: {}", green);
    println!("Blue value: {}\n", blue);
    let orange = Color(255, 165, 0);
    // You can also destructure the fields directly
    let Color(r, g, b) = orange;
    println!("R: {}, G: {}, B: {} (orange)", r, g, b);
    // Can also ignore fields while destructuring
    let Color(r, _, b) = orange;
}

  • Normal struct
struct Player {
    name: String,
    iq: u8,
    friends: u8,
    score: u16,
}
fn bump_player_score(mut player: Player, score: u16) {
    player.score += 120;
    println!("Updated player stats:");
    println!("Name: {}", player.name);
    println!("IQ: {}", player.iq);
    println!("Friends: {}", player.friends);
    println!("Score: {}", player.score);
}
fn main() {
    let name = "Alice".to_string();
    let player = Player {
        name,
        iq: 171,
        friends: 134,
        score: 1129,
    };
    bump_player_score(player, 120);
}
  • Enums
enum Direction {
    N,
    E,
    S,
    W,
}
enum PlayerAction {
    Move { direction: Direction, speed: u8 },
    Wait,
    Attack(Direction),
}
fn main() {
    let simulated_player_action = PlayerAction::Move {
        direction: Direction::N,
        speed: 2,
    };
    match simulated_player_action {
        PlayerAction::Wait => println!("Player wants to wait"),
        PlayerAction::Move { direction, speed } => println!("Player wants to move"),
        PlayerAction::Attack(_) => todo!(),
    }
}

Impl trong struct

  • Instance methodsassociated methods
struct Player {
    name: String,
    iq: u8,
    friends: u8,
}
impl Player {
    fn with_name(name: &str) -> Player {
        Player {
            name: name.to_string(),
            iq: 100,
            friends: 100,
        }
    }
    fn get_friends(&self) -> u8 {
        self.friends
    }
    fn set_friends(&mut self, count: u8) {
        self.friends = count;
    }
}
fn main() {
    let mut player = Player::with_name("Dave");
    player.set_friends(23);
    println!("{}'s friends count: {}", player.name, player.get_friends());
    // another way to call instance methods.
    let _ = Player::get_friends(&player);
}

  • self as the first parameter. In this case, calling this method won't allow you to use the type later.
  • &self as the first parameter. This method only provides read access to the instance of a type.
  • &mut self as the first parameter. This method provides mutable access to the instance of a type.

Impl trong enum

enum PaymentMode {
    Debit,
    Credit,
    Paypal,
}
// Bunch of dummy payment handlers
fn pay_by_credit(amt: u64) {
    println!("Processing credit payment of {}", amt);
}
fn pay_by_debit(amt: u64) {
    println!("Processing debit payment of {}", amt);
}
fn paypal_redirect(amt: u64) {
    println!("Redirecting to paypal for amount: {}", amt);
}
impl PaymentMode {
    fn pay(&self, amount: u64) {
        match self {
            PaymentMode::Debit => pay_by_debit(amount),
            PaymentMode::Credit => pay_by_credit(amount),
            PaymentMode::Paypal => paypal_redirect(amount),
        }
    }
}
fn get_saved_payment_mode() -> PaymentMode {
    PaymentMode::Debit
}
fn main() {
    let payment_mode = get_saved_payment_mode();
    payment_mode.pay(512);
}

Modules, imports, use keyword

Collections

  • Arrays
fn main() {
    let numbers: [u8; 10] = [1, 2, 3, 4, 5, 7, 8, 9, 10, 11];
    let floats = [0.1f64, 0.2, 0.3];
    println!("Number: {}", numbers[5]);
    println!("Float: {}", floats[2]);
}
  • tupple
 fn main() {
       let num_and_str: (u8, &str) = (40, "Have a good day!");
       println!("{:?}", num_and_str);
       let (num, string) = num_and_str;
       println!("From tuple: Number: {}, String: {}", num, string);
}
  • vector
fn main() {
    let mut numbers_vec: Vec<u8> = Vec::new();
    numbers_vec.push(1);
    numbers_vec.push(2);
    let mut vec_with_macro = vec![1];
    vec_with_macro.push(2);
    let _ = vec_with_macro.pop(); // value ignored with `_`
    let message = if numbers_vec == vec_with_macro {
        "They are equal"
    } else {
        "Nah! They look different to me"
    };
    println!("{} {:?} {:?}", message, numbers_vec, vec_with_macro);
}

  • Hash mapss
use std::collections::HashMap;
fn main() {
    let mut fruits = HashMap::new();
    fruits.insert("apple", 3);
    fruits.insert("mango", 6);
    fruits.insert("orange", 2);
    fruits.insert("avocado", 7);
    for (k, v) in &fruits {
        println!("I got {} {}", v, k);
    }
    fruits.remove("orange");
    let old_avocado = fruits["avocado"];
    fruits.insert("avocado", old_avocado + 5);
    println!("\nI now have {} avocados", fruits["avocado"]);
}

  • Slices
fn main() {
    let mut numbers: [u8; 4] = [1, 2, 3, 4];
    {
        let all: &[u8] = &numbers[..];
        println!("All of them: {:?}", all);
    }
    {
        let first_two: &mut [u8] = &mut numbers[0..2];
        first_two[0] = 100;
        first_two[1] = 99;
    }
    println!("Look ma! I can modify through slices: {:?}", numbers);
}

  • Iterators

Bài tập về nhà:

Làm cho đoạn code sau chạy được


use std::env;
use std::fs::File;
use std::io::prelude::BufRead;
use std::io::BufReader;
#[derive(Debug)]
struct WordCounter(HashMap<String, u64>);
impl WordCounter {
    fn new() -> WordCounter {
        WordCounter(HashMap::new());
    }
    fn increment(word: &str) {
        let key = word.to_string();
        let count = self.0.entry(key).or_insert(0);
        *count += 1;
    }
    fn display(self) {
        for (key, value) in self.0.iter() {
            println!("{}: {}", key, value);
        }
    }
}
fn main() {
    let arguments: Vec<String> = env::args().collect();
    let filename = arguments[1];
    println!("Processing file: {}", filename);
    let file = File::open(filenam).expect("Could not open file");
    let reader = BufReader::new(file);
    let mut word_counter = WordCounter::new();
    for line in reader.lines() {
        let line = line.expect("Could not read line");
        let words = line.split(" ");
        for word in words {
            if word == "" {
                continue;
            } else {
                word_counter.increment(word);
            }
        }
    }
    word_counter.display();
}