[Rust Serial] Bài 1: Cargo Managmement và một tour về ngôn ngữ
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
-
Dependencies từ
-
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.toml
vàCargo.lock
nằm tạipackage 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ếnCARGO_TARGET_DIR
[build] target-dir = "/path/to/your/target" Hoặc 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ấpcargo 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/
vàtests/
CI/CD
Build Cache
target/debug/
: Cho debugtarget/release/
: Cho releasetarget/foo
: Cho profilefoo
. 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
dev
vàrelease
- 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 cratesCrates
: A tree of modules that produces a library or executableModules
and use: Let you control the organization, scope, and privacy of pathsPaths
: A way of naming an item, such as a struct, function, or moduleTip 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 crates
vàlibrary 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ềucrates
mà cung cấp một tập hợp các chức năng. Mộtpackage
sẽ bao gồm mộtCargo.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.rs
và 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
- Một package có thể gồm chỉ một trong 2 files
-
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 filesrc/lib.rs
hoặcsrc/main.rs
Declaring modules
: Eg:mod foo;
. Có thể có 3 khả năngsrc/foo.rs
hoặcsrc/foo/mod.rs
hoặc tronglib.rs
chứamod foo{}
Declaring submodules
: Chúng ta có thểsubmodule
bên trong nới chưa fileCargo.toml
bên trong- Path đến module
crate::foo::func1
. Path có thể làabsolute path
hoặcrelative path
- Có thể có
private
vàpublic
: Sử dụng từ khoápub
- Sử dụng từ khoá
use
để sử dụng Link
Path
absolute path
: Như sử dụngcrate
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
trongpub 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ụng
use
để 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ặccrate-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ụngdefault
để 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 trongdefault-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ộcserde
trong crates.io vàrgb
crate cũng cho phép featureserde
. - 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 đặtcargo 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
,test
vàbench
-
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ơnlto
: link time optimize. Eg:false
: thin LTO,true
orfat
: 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
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
- 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ởicargo 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ụngcargo 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ấuu8
,u16
,u32
,u64
,u128
isize
vàusize
phụ thuộc vào hệ điều hànhf32
,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 Tstr
: String slices sử dụng như tham chiếu(T, U, ..)
: Chuối vô hãnfn(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 methods
vàassociated 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();
}