Loạt bài viết giới thiệu các khái niệm trong Rust. Không quá chi tiết, đa phần ngắn gọn, mục tiêu trong 24 ngày.
PS: Là note khi giải https://adventofcode.com
Cài đặt & chạy code
Tham khảo https://pp.pymi.vn/article/aoc2021/
Bài viết sử dụng Rust 2021.
String, &str
Rust có 2 kiểu string thường dùng là str và String.
str - string slice
str
còn được gọi là string slice, thường được dùng ở dạng "borrowed" &str
.
Kiểu của các string khi viết trực tiếp vào code (literal string) là &str
.
str
dùng double quote "
để bao quanh nội dung:
let s: &str = "Hello, world!";
String
String
là một UTF-8 string có thể thay đổi.
A UTF-8–encoded, growable string.
String
làm chủ sở hữu giá trị của nó nên còn gọi là owned string - trái với &str
là borrowed (đi mượn).
let s: String = String::from("Hello, world!");
dbg!(s);
// [src/main.rs:6] s = "Hello, world!"
Thêm (append) vào String với push(char)
hay push_str(&str)
.
let mut res: String = String::from("Hello, world!");
res.push('\t');
res.push_str("How are you?");
// [src/main.rs:10] res = "Hello, world!\tHow are you?"
String vs &str
String
và &str
có nhiều method giống nhau: starts_with
, ends_with
, split
, replace
, is_digit
...
Sự khác biệt chủ yếu ở mục đích sử dụng:
- Dùng
String
khi tạo string mới hay nội dung string thay đổi. - Dùng
&str
khi nội dung string cố định, không mới.
Method replace
cho thấy sự khác biệt này:
pub fn replace<'a, P>(&'a self, from: P, to: &str) -> String
where
P: Pattern<'a>,
"a b c a".replace("a", "d")
trả về 1 String
. Có thể hiểu rằng do Rust cần cấp phát bộ nhớ để tạo ra 1 String có nội dung mới, nên đây là kiểu String
thay vì &str
.
Đọc nội dùng 1 file cũng trả về String:
let contents = std::fs::read_to_string("/etc/passwd").unwrap();
pub fn read_to_string<P>(path: P) -> io::Result<String>
where
P: AsRef<Path>,
Dùng "abc".to_string()
để biến &str
thành String
, ngược lại String::from("abc").as_str()
để biến String
thành &str
.
Split
"a-b-c".split("-")
trả về một Split
struct, hay full name std::str::Split
chứ không phải 1 vector Vec<&str>
.
pub fn split<'a, P>(&'a self, pat: P) -> Split<'a, P>
where
P: Pattern<'a>,
Split
implement Iterator
trait (hay nói như Python: list là 1 iterable, có method __iter__
)
nên có thể duyệt qua từng phần tử của kết quả, mỗi phần tử là 1 &str
cho dù s
là &str
hay String
:
let sp: std::str::Split<&str> = "bacadae".split("a");
for p in sp {
dbg!(p);
}
let s = String::from("bacadae");
for p in s.split("a") {
dbg!(p);
}
//[src/main.rs:16] p = "b"
//[src/main.rs:16] p = "c"
//[src/main.rs:16] p = "d"
//[src/main.rs:16] p = "e"
filter
method trả về 1 Filter
let s = String::from("bacadae");
// Filter<Split<&str>, |&&str| -> bool>
let ps = s.split("a").filter(|&s| s > "c");
for r in ps {
dbg!(r); // r là &str
}
// [src/main.rs:17] r = "d"
// [src/main.rs:17] r = "e"
Để thu được Vec<&str>
dùng collect()
:
let s = " a b\tc\nd ";
// pub fn split_whitespace(&self) -> SplitWhitespace<'_>
let parts: Vec<&str> = s.split_whitespace().collect();
dbg!(parts);
// [src/main.rs:19] parts = [
// "a",
// "b",
// "c",
// "d",
// ]
char - character
char là một ký tự, hay chính xác hơn là 1 Unicode scalar value, dùng single quote '
để bao quanh 2 bên char.
let c: char = '\u{1b0}'; // ư
let c2 = 'a';
Biến vector char thành String với collect
:
let v = vec!['a', 'b', 'c'];
let s: String = v.iter().collect();
Kết luận
Khi nội dung string được tạo mới, hoặc thay đổi, dùng String
, khi nội dung cố định, không mới, dùng &str
.