Trong bài viết này mình sẽ hướng dẫn các bạn học và làm quen với ngôn ngữ Rust trong 1 bài viết duy nhất. Các bạn cần có kỹ năng lập trình với các ngôn ngữ khác trước khi đọc bài viết này. Nếu không sẽ thấy khó hiểu. Còn đã từng tiếp xúc ngôn ngữ khác rồi mà vẫn thấy khó hiểu thì chắc là tại bài viết này mình viết hơi khó hiểu 🙁(( *buồn
Rust là gì ?
Là một ngôn ngữ lập trình hệ thống (system programming language) Theo quảng cáo là có tốc độ cực kì nhanh, ngăn chặn được phần lớn bug crash (segfaults), ngăn chặn được data race, rất memory safe.
7 lý do chọn RUST
- Rust giải quyết các vấn đề quản lý bộ nhớ
- Chi phí thấp của Rust làm cho nó trở nên lý tưởng cho lập trình nhúng
- Rust Facilitates Powerful Web Application Development
- Rust’s Static Typing đảm bảo khả năng bảo trì dễ dàng
- Rust có hiệu suất nhanh và cao (Rust ngang bằng với C ++ và đánh bại các ngôn ngữ như Python)
- Hỗ trợ và phát triển đa nền tảng
- Rust có một hệ sinh thái mở rộng
Cài đặt rust
Bật terminal và gõ những lệnh sau, lệnh đầu tiên để tải rust về, lệnh 2 để active env
tạm gọi là kích hoạt môi trường. Lệnh thứ 3 là để update phiên bản mới nhất của rust.
$ curl https://sh.rustup.rs -sSf | sh
$ source $HOME/.cargo/env
$ rustup update
Cargo: công cụ xây dựng (build tool) Rust và trình quản lý gói (package manager)
Khi cài đặt Rustup, bạn cũng sẽ nhận được phiên bản ổn định mới nhất của Rust build tool và trình quản lý gói, còn được gọi là Cargo. Cargo làm được nhiều việc:
- Build project của bạn với:
cargo build
- Chạy project của bạn với:
cargo run
- Test project của bạn với:
cargo test
- Xây dựng tài liệu cho dự án của bạn với:
cargo doc
- Xuất bản thư viện lên crates.io với:
cargo publish
Để kiểm tra xem bạn đã cài đặt Rust and Cargo chưa, bạn có thể chạy lệnh này trong cửa sổ dòng lệnh terminal của bạn:
$ cargo --version
Tạo một dự án mới
Để tạo một dự án mới với tên hello-rust
các bạn gõ lệnh sau thay tên thành tên project của bạn
cargo new hello-rust
Thư mục và cấu trúc file như sau sẽ được tạo ra tự động
hello-rust
|- Cargo.toml
|- src
|- main.rs
Cargo.toml
là tệp manifest cho Rust. Đó là nơi bạn lưu giữ metadata cho dự án của mình cũng như các phần phụ thuộc.
src/main.rs
là nơi chúng ta sẽ viết mã ứng dụng của mình.
cargo run
Để chạy project các bạn vừa tạo. Lưu ý con trỏ thư mục làm việc ở terminal phải ở trong thư mục code mà chúng ta vừa tạo. Nếu không thì bạn có thể chuyển vào bằng lệnh cd hello-rust
cú pháp chung cd path/to/project
Chúng ta chạy:
cargo run
Kết quả:
$ cargo run
Compiling hello-rust v0.1.0 (/Users/ag_dubs/rust/hello-rust)
Finished dev [unoptimized + debuginfo] target(s) in 1.34s
Running `target/debug/hello-rust`
Hello, world!
Thêm thư viện trong Rust
Hãy thêm phần phụ thuộc vào ứng dụng của chúng ta. Bạn có thể tìm thấy tất cả các loại thư viện trên crates.io. Trong Rust, chúng thường gọi các gói là crates.
Trong dự án này, chúng ta sẽ sử dụng một cái crates có tên là: ferris-says.
Trong tệp Cargo.toml
, chúng ta sẽ thêm thông tin này:
[dependencies]
ferris-says = "0.2"
Bây giờ chúng ta có thể chạy:
cargo build
và Cargo sẽ cài đặt phụ thuộc(dependency) cho chúng ta.
Bạn sẽ thấy rằng việc chạy lệnh này đã tạo ra một tệp mới cho chúng ta, Cargo.lock
. Tệp này là nhật ký của các phiên bản của thư viện chính xác mà chúng ta đang sử dụng ở local.
Để sử dụng phần phụ thuộc này, chúng ta có thể mở main.rs
và thêm dòng này vào đầu file đó:
use ferris_says::say;
Dòng này có nghĩa rằng chúng ta sẽ thêm hàm say
của crate ferris-says
vào dự án.
Một ứng dụng Rust nhỏ
Bây giờ chúng ta hãy viết một ứng dụng nhỏ với thư viện của chúng ta. Trong main.rs
của chúng tôi, hãy thêm mã sau:
use ferris_says::say; // from the previous step
use std::io::{stdout, BufWriter};
fn main() {
let stdout = stdout();
let message = String::from("Hello fellow Rustaceans!");
let width = message.chars().count();
let mut writer = BufWriter::new(stdout.lock());
say(message.as_bytes(), width, &mut writer).unwrap();
}
Sau khi lưu, chúng ta có thể chạy ứng dụng của mình bằng cách gõ:
cargo run
Giả sử mọi thứ diễn ra tốt đẹp, bạn sẽ thấy ứng dụng của mình in ra màn hình:
----------------------------
| Hello fellow Rustaceans! |
----------------------------
\
\
_~^~^~_
\) / o o \ (/
'_ - _'
/ '-----' \
bây giờ chúng ta sẽ đi vào chi tiết hơn từng thành phần
Kiểu dữ liệu nguyên thuỷ trong rust
Scalar Types
- Số nguyên có dấu :
i8
, i16
, i32
, i64
, i128
và isize
(Kích thước con trỏ)
- Số nguyên không dấu:
u8
, u16
, u32
, u64
, u128
and usize (pointer size)
- Số thực:
f32
, f64
char
Unicode scalar giá trị dạng 'a'
, 'α'
và '∞'
(4 bytes)
- Dữ liệu kiểu bool
true
or false
- Kiểu dữ liệu đơn vị
()
, có giá trị duy nhất là một tuple rỗng: ()
Compound Types
Kiểu mảng arrays [1, 2, 3]
Kiểu tuples (1, true)
Các con số cũng có thể được chú thích thông qua một hậu tố hoặc theo mặc định. Các số nguyên mặc định là i32
và float là f64
. Lưu ý rằng Rust cũng có thể suy ra kiểu dữ liệu từ ngữ cảnh sử dụng.
// Variables can be type annotated.
let logical: bool = true;
let a_float: f64 = 1.0; // Regular annotation
let an_integer = 5i32; // Suffix annotation
// Or a default will be used.
let default_float = 3.0; // `f64`
let default_integer = 7; // `i32`
// A type can also be inferred from context
let mut inferred_type = 12; // Type i64 is inferred from another line
inferred_type = 4294967296i64;
// A mutable variable's value can be changed.
let mut mutable = 12; // Mutable `i32`
mutable = 21;
// Error! The type of a variable can't be changed.
mutable = true;
// Variables can be overwritten with shadowing.
let mutable = true;
Custom Types
Các kiểu dữ liệu tùy chỉnh Rust được hình thành chủ yếu thông qua hai từ khóa:
struct
: xác định cấu trúc
enum
: xác định một kiểu liệt kê
Hằng số cũng có thể được tạo thông qua từ khóa const
và static
.
Chuyển đổi kiểu dữ liệu
dữ liệu nguyên thuỷ
let decimal1 = 65.4321_f32;
// Explicit conversion
let integer1 = decimal1 as u8;
let character1 = integer1 as char;
Chuyển đổi kiểu dữ liệu sử dụng from. Ví dụ, chúng ta có thể dễ dàng chuyển đổi một str thành một String
let my_str = "hello";
let my_string = String::from(my_str);
Chuyển đổi chuỗi thành số
let parsed: i32 = "5".parse().unwrap();
let turbo_parsed = "10".parse::<i32>().unwrap();
let sum = parsed + turbo_parsed;
println!("Sum: {:?}", sum);
Chuyển đổi bất kỳ kiểu nào thành chuỗi cũng giống như việc triển khai hàm ToString cho kiểu:
use std::fmt;
struct Circle {
radius: i32
}
impl fmt::Display for Circle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Circle of radius {}", self.radius)
}
}
fn main() {
let circle = Circle { radius: 6 };
println!("{}", circle.to_string());
}
Flow of Control
Biểu thức điều kiện if else trong rust
Việc phân nhánh với if-else cũng tương tự như các ngôn ngữ khác. Nhưng biểu thức điều kiện không cần để trong cặp ngoặc tròn ()
.
if n < 0 {
print!("{} is negative", n);
} else if n > 0 {
print!("{} is positive", n);
} else {
print!("{} is zero", n);
}
vòng lặp trong rust
Câu lệnh break
có thể được sử dụng để thoát khỏi một vòng lặp bất cứ lúc nào, trong khi câu lệnh continue
có thể được sử dụng để bỏ qua phần còn lại của lần lặp và bắt đầu một vòng lặp mới.
fn main() {
let mut count = 0u32;
println!("Let's count until infinity!");
// Infinite loop
loop {
count += 1;
if count == 3 {
println!("three");
// Skip the rest of this iteration
continue;
}
println!("{}", count);
if count == 5 {
println!("OK, that's enough");
// Exit this loop
break;
}
}
}
có thể thoát các vòng lặp lồng nhau bằng label
Có thể break
hoặc continue
các vòng lặp bên ngoài khi xử lý các vòng lặp lồng nhau. Trong những trường hợp này, các vòng lặp phải được chú thích bằng một 'labelvà label phải được gán cho câu lệnh
break / continue`.
#![allow(unreachable_code)]
fn main() {
'outer: loop {
println!("Entered the outer loop");
'inner: loop {
println!("Entered the inner loop");
// This would break only the inner loop
//break;
// This breaks the outer loop
break 'outer;
}
println!("This point will never be reached");
}
println!("Exited the outer loop");
}
Kết quả:
Entered the outer loop
Entered the inner loop
Exited the outer loop
Returning from loops
Một trong những cách sử dụng của vòng lặp là thử lại một hoạt động cho đến khi nó thành công. Nếu bạn muốn sau khi vòng lặp kết thúc trả về một giá trị bạn có thể đặt nó sau lệnh break
:
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
vòng lặp while
Điều kiện còn đúng thì còn lặp. Nó tương tự như các ngôn ngữ lập trình khác.
// Loop while `n` is less than 101
while n < 101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
// Increment counter
n += 1;
}
for trong một khoảng
Cấu trúc for in
có thể được sử dụng để lặp lại qua một Iterator. Một trong những cách dễ nhất để tạo một trình lặp là sử dụng ký hiệu phạm vi a..b
. Điều này mang lại các giá trị từ a (bao gồm a) đến b (loại trừ b) trong các bước của vòng lặp.
// `n` will take the values: 1, 2, ..., 100 in each iteration
for n in 1..101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
}
Function trong rust
Các hàm được khai báo bằng từ khóa fn
. Các đối số của nó là kiểu được chú thích, giống như các biến và nếu hàm trả về một giá trị kiểu trả về phải được chỉ định sau một mũi tên ->
// Function that returns a boolean value | hàm trả về giá trị
fn is_divisible_by(lhs: u32, rhs: u32) -> bool {
// Corner case, early return
if rhs == 0 {
return false;
}
// This is an expression, the `return` keyword is not necessary here
lhs % rhs == 0
}
// Functions that "don't" return a value, actually return the unit type `()`
// hàm không trả về 1 value nhưng trả về 1 unit type
fn fizzbuzz(n: u32) -> () {
if is_divisible_by(n, 15) {
println!("fizzbuzz");
} else if is_divisible_by(n, 3) {
println!("fizz");
} else if is_divisible_by(n, 5) {
println!("buzz");
} else {
println!("{}", n);
}
}
// When a function returns `()`, the return type can be omitted from the
// signature | bỏ dấu mũi tên nếu nó return về `()`
fn fizzbuzz_to(n: u32) {
for n in 1..n + 1 {
fizzbuzz(n);
}
}
Method trong Rust
Phương thức là các hàm gắn liền với các đối tượng. Các phương thức này có quyền truy cập vào dữ liệu của đối tượng và các phương thức khác của nó thông qua từ khóa self
. Các phương thức được định nghĩa dưới một khối impl
.
struct Point {
x: f64,
y: f64,
}
// Implementation block, all `Point` methods go in here
impl Point {
// This is a static method
// Static methods don't need to be called by an instance
// These methods are generally used as constructors
fn origin() -> Point {
Point { x: 0.0, y: 0.0 }
}
// Another static method, taking two arguments:
fn new(x: f64, y: f64) -> Point {
Point { x: x, y: y }
}
}
Closures trong rust | Bao đóng trong rust
Closures (Bao đóng) là hàm có thể nắm bắt môi trường bao quanh(capture the enclosing environment). Gọi một bao đóng cũng giống như gọi một hàm.
Nếu bạn từng học javascript thì bao đóng trong javascript cung cấp cho bạn quyền truy cập vào phạm vi (scope) của một hàm bên ngoài từ một hàm bên trong. (Hi vọng mình dịch dễ hiểu: a closure gives you access to an outer function’s scope from an inner function).
Các bao đóng của Rust là các hàm ẩn danh mà bạn có thể lưu trong một biến hoặc chuyển làm đối số cho các hàm khác. Bạn có thể tạo bao đóng ở một nơi và sau đó gọi nó trong một ngữ cảnh khác. Không giống như các hàm, các bao đóng có thể nắm bắt (capture) các giá trị từ phạm vi (scope) mà chúng được xác định. Đọc thêm và xem đầy đủ tại đây
sự tương đồng của bao đóng với các hàm thông thường
// hàm viết kiểu thông thường
fn function(i: i32) -> i32 { i + 1 }
// cho hiệu quả tương tự
let closure_annotated = |i: i32| -> i32 { i + 1 };
let closure_inferred = |i | i + 1 ;
Khi gọi các hàm này
let i = 1;
// Call the function and closures.
println!("function: {}", function(i));
println!("closure_annotated: {}", closure_annotated(i));
println!("closure_inferred: {}", closure_inferred(i));
Kết quả:
function: 2
closure_annotated: 2
closure_inferred: 2
Traits trong rust
Trait là một tập hợp các phương thức được xác định cho một kiểu chưa biết: Self
có thể truy cập các phương thức khác được khai báo trong cùng một trait
(nhìn trông giống class =)) )
trait Animal {
// Static method signature; `Self` refers to the implementor type.
fn new(name: &'static str) -> Self;
// Instance method signatures; these will return a string.
fn name(&self) -> &'static str;
fn noise(&self) -> &'static str;
// Traits can provide default method definitions.
fn talk(&self) {
println!("{} says {}", self.name(), self.noise());
}
}
Modules trong rust
Rust cung cấp một hệ thống mô-đun mạnh mẽ có thể được sử dụng để phân chia theo thứ bậc mã trong các đơn vị logic (mô-đun) và quản lý khả năng hiển thị (công khai / riêng tư) giữa chúng.
cách khai báo và sử dụng module
mod my {
// A public struct with a public field of generic type `T`
pub struct OpenBox<T> {
pub contents: T,
}
// A public struct with a private field of generic type `T`
#[allow(dead_code)]
pub struct ClosedBox<T> {
contents: T,
}
impl<T> ClosedBox<T> {
// A public constructor method
pub fn new(contents: T) -> ClosedBox<T> {
ClosedBox {
contents: contents,
}
}
}
}
fn main() {
// Public structs with public fields can be constructed as usual
let open_box = my::OpenBox { contents: "public information" };
// and their fields can be normally accessed.
println!("The open box contains: {}", open_box.contents);
// Public structs with private fields cannot be constructed using field names.
// Error! `ClosedBox` has private fields
//let closed_box = my::ClosedBox { contents: "classified information" };
// TODO ^ Try uncommenting this line
// However, structs with private fields can be created using
// public constructors
let _closed_box = my::ClosedBox::new("classified information");
// and the private fields of a public struct cannot be accessed.
// Error! The `contents` field is private
//println!("The closed box contains: {}", _closed_box.contents);
// TODO ^ Try uncommenting this line
}
Kết bài
Về cơ bản mình đã cung cấp thông tin những thứ thường gặp nhất. Tất nhiên là không thể hết được tất cả các vấn đề nhưng nếu bạn đã từng lập trình và muốn học nhanh một ngôn ngữ rồi bắt tay vào học trong khi làm thì bạn có thể tham khảo bài viết này của mình. Nó sẽ giúp bạn tổng hợp những vấn đề quan trọng nhất.