มาหัดเขียนโปรแกรมด้วยภาษา Rust กันเถอะ

Published on
Rust
getting-started-with-rust
Discord

สวัสดีครับ ช่วงนี้ผมสนใจอยากเขียนภาษา Rust ก็เลยเริ่มต้นศึกษา เวลาว่างๆ วันละ ครึ่งชม. หรือ 1ชั่วโมงบ้าง แล้วแต่วัน เลยอยากจะเขียนบล็อกบันทึกไว้ สืบเนื่องจาก ได้ยินชื่อภาษานี้มาหลายปีแล้ว แต่ไม่มีโอกาสได้ใช้งาน ก็เลยผ่านไป

แต่ช่วงนี้เป็นช่วงที่เริ่มมีเวลาศึกษามากขึ้น อาจจะเพราะว่าหยุดเขียนโค๊ดไปนาน ช่วงนี้มันเลยมีแรงจูงใจนิดนึง ตรงที่รู้สึกว่าเราไม่รู้อะไรเต็มไปหมดเลย ก็เลยอยากจะลองหัด Rust ซะ ซึ่งจริงๆแล้วช่วงนี้ผมหัดไปทั่วมากครับ ลองๆให้หมด ทั้ง Solidity / Blockchain กลับไปอ่าน C/C++ นั่งโค๊ด Python เล่นๆ แล้วค่อยมาดูอีกทีว่าจะโฟกัส หรือลงลึกไปที่ภาษาไหน ซึ่งมันก็ขึ้นอยู่กับตัวงานด้วย😅

ติดตั้ง Rustup

สำหรับ Mac OS และ Unix นั้นติดตั้งง่ายมากๆ เข้าไปในเว็บ rustup

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

ส่วน Windows จะชื่อไฟล์ rustup-init.exe ดาวน์โหลดมา Install ได้เลย เป็น Script ที่ติดตั้ง package และจัดการ Path ให้เราเรียบร้อย ไม่ต้องทำอะไรเลย แค่ restart ตัว cmd.exe หรือ Powershell ก็สามารถใช้งาน Rust ได้แล้ว

เมื่อติดตั้งเสร็จเราจะได้ทั้ง rustc ที่เป็น Compiler, Cargo ที่เป็น Package Manager และ rustup มาพร้อมๆกันเลย

ทดสอบดูว่า ติดตั้ง Rustup เรียบร้อย พร้อม rustc ที่ใช้งานได้

rustc --version

# เวอร์ชั่น ณ​ วันที่เขียนบทความ
rustc 1.59.0 (9d1b2106e 2022-02-23)

ไฟล์ config ของ Rust environment จะอยู่ที่ ~/.cargo/bin ส่วน Windows ก็คือ %USERPROFILE%\.cargo\bin

หรือหากเราเคยติดตั้ง Rustup มาแล้ว อยากจะอัพเดท ก็สามารถอัพเดทได้ด้วยคำสั่ง

rustup update

Text Editor

แน่นอน VS Code น่าจะกลายเป็น standard และคนนิยมที่สุดแล้วครับ ส่วน Extension ก็ลง Rust-Analyzer

แต่ถ้าใครใช้ IDE อย่างเช่น ของ JetBrains ก็ต้องใช้ตัวนี้ Plugin Rust (คิดว่าน่าจะใช้ได้ทั้ง CLion และ IntelijIDEA)

Hello World แรก

ทดลองเขียนโปรแกรมแรกกันเลย ตั้งชื่อไฟล์ว่า hello.rs

hello.rs
fn main() {
  println!("Hello World!");
}

จากนั้น Compile เพื่อให้ได้ binary file เพื่อ execute มันได้

rustc hello.rs

จะได้ไฟล์ Output ชื่อเดียวกับไฟล์ คือ hello บน Windows คือ hello.exe จากนั้นลอง execute มันดู

./hello

Hello World!

หาก Compile ไม่ผ่าน จะมี Error บอก (ลองลบ ! ออกเหลือแค่ println ดูครับ)

error[E0423]: expected function, found macro `println`
 --> hello.rs:2:5
  |
2 |     println("Hello World");
  |     ^^^^^^^ not a function
  |
help: use `!` to invoke the macro
  |
2 |     println!("Hello World");
  |            +

error: aborting due to previous error

จะเห็นได้ว่า Compile error message ก็มีประโยชน์มากๆ ทำให้เรารู้ว่า มันมีปัญหาอะไร บรรทัดไหน (สำหรับมือใหม่ ที่ไม่เคยเขียนโปรแกรม อาจจะกลัวๆ การอ่าน error message ลองพยายามอ่าน และทำความเข้าใจดูนะครับ ไม่ยาก)

Cargo เบื้องต้น

หลังจากลอง โปรแกรมแรกไปละ คราวนี้มาลองสร้างโปรเจ็คด้วยการใช้ Cargo บ้าง

การสร้างโปรเจ็คใหม่ ใช้คำสั่ง cargo new <NAME> เช่น

cargo new hello-rust

# จะได้ผลลัพธ์
Created binary (application) `hello-rust` package

ตัว Cargo จะทำการสร้างโฟลเดอร์ hello-rust ขึ้นมา พร้อมกับไฟล์ src/main.rs และ Cargo.toml ดังนี้

.
├── Cargo.toml
└── src
    └── main.rs
  • Cargo.toml - เป็นเหมือน metadata ของโปรเจ็คเรา (เรียกว่า manifest) รู้ว่าใช้ library อะไรบ้าง หากใครเขียน Node.js มาก่อน ก็เหมือนกับ package.json ครับ
  • src/main.rs - ไฟล์หลัก จะเห็นว่า เหมือนกับเราสร้างไฟล์ปกติเลย

ตัวอย่างข้างใน Cargo.toml จะเห็นว่ามี name มี version มี editor และส่วน dependencies

Cargo.toml
[package]
name = "hello-rust"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

ทีนี้เวลา compile เราไม่ต้องใช้ rustc แล้ว เราใช้คำสั่ง cargo run ครับ (จริงๆ มันใช้ rustc นั่นแหละ แต่ Cargo รันให้เรา)

cargo run

จะได้ผลลัพธ์ประมาณนี้

Compiling hello-rust v0.1.0 (/Users/chai/dev/hello-rust)
Finished dev [unoptimized + debuginfo] target(s) in 0.27s
  Running `target/debug/hello-rust`
Hello, world!

ไฟล์จะถูก compile ไว้ที๋โฟลเดอร์ target เราลอง execute ตัว binary ไฟล์ดูได้

./target/debug/hello-rust

# ผลลัพธ์
Hello, world!

ใช้คำสั่ง cargo build เพื่อ compile และ build package

cargo build

ข้อแตกต่างระหว่าง cargo build และ cargo run คือ

  • cargo run จะ compile ถ้ายังไม่ถูก compile และ run โปรแกรม
  • cargo build จะ compile และ build เฉยๆ ไม่ได้รันโปรแกรม

หากอยากลบไฟล์ต่างๆ พวก binary ที่ compiled ไว้ ก็สามารถลบได้ด้วยคำสั่ง

cargo clean

การติดตั้ง Dependencies เช่น ติดตั้ง rand ที่เป็น random generator บน crates.io ก็เพิ่มไปในไฟล์ Cargo.toml ได้เลย

[dependencies]
rand = "0.8.5"

เวลา build มันก็จะไปทำการดาวน์โหลด library มาติดตั้ง ก่อน compile ครับ

สุดท้าย Build ที่เราเห็น จะสังเกตเห็นว่า มัน unoptimized เนื่องจากว่ามัน compile และ build binary ที่มีพวก debugging information หรืออะไรเต็มไปหมด ถ้าเราอยาก optimized / production ก็ใช้ --release :

cargo build --release

Compiling hello-rust v0.1.0 (/Users/chai/dev/hello-rust)
  Finished release [optimized] target(s) in 0.11s

สำหรับ Cargo ก็มีหนังสือนะครับ ไปอ่านเพิ่มเติมได้ครับ The Cargo Book

Rust แบบรวบรัด สำหรับคนมีพื้นฐานภาษาอื่นๆ

  • การประกาศตัวแปร ใช้ let ชื่อตัวแปรจะใช้แบบ snake_case
  • มีการใช้ const เวลาประกาศค่าคงที่
  • ปิด statment ด้วย semicolon
  • ฟังค์ชั่น ใช้ keyword fn ดังที่เห็นตอน Hello World แรก
  • println! เป็น marco ที่เอาไว้แสดงค่า
fn main() {
    let number = 99;
    let second_variable = 999;
    let mut a_number = 1;

    println!("second_variable is {}", second_variable);

    a_number = 2;

    println!("a_number is {}", a_number);
}

ผลลัพธ์

second_variable is 999
a_number is 2

ประกาศตัวแปร แบบกำหนด type รองรับ type ต่างๆ เช่น i8, i16, i32, i64, i128 และ unsigned integer

let x:i32 = 1;
let y:u32 = 14;

Float ตัว compiler จะใช้ f64 นอกจากจะกำหนด type ให้มัน

let thailand = 3.0;
let thailand_f: f32 = 4.0;

Boolean true กับ false

let is_odd = 2 % 2 == 0;
println!("2 is odd? {}", is_odd);

Character และ String

// char
let i_am_a = 'a';

// string literal
let s: &str = "Hello World!";

// string allocated memory
let hello: &'static str = "hello";

// ใช้ String::new()
let mut string = String::new();
string.push('H');

// Heap
let greeting = String::from("Hello World");

If/Else ก็เหมือนภาษาอื่นๆ

let score = 81;

if score > 49 {
  println!("Passed!");
} else {
  println!("F!");
}

Loop

for i in 0..10 {
  println!("{}", i);
}

let mut i = 0;

while i < 10 {
    println!("hello");
    i = i + 1;
}

Array ข้อมูลที่เก็บใน collection ต้องเป็น type เดียวกันและมีขนาดแน่นอน ตัวอย่าง กำหนด type และขนาด array:

let nums: [i32; 3] = [1, 2, 3];

Tuple เหมือนกับ Array ต่างกันที่ค่าข้างใน collection ต่างชนิดกันได้

let my_tuple = ('A', 10, true);

Struct คล้ายๆ tuple ข้างในมี fields ที่ต่าง type กันได้

// Struct แบบมี fields
struct Worker {
  name: String,
  department: String,
  remote: bool
}

// Struct แบบ Unit struct
struct Unit;

// Struct แบบ มีแต่ type (Tuple struct)
struct OnlyYou(char, bool);

รองรับ Enum เหมือนภาษาอื่นๆ

enum Keyboard {
  KEYUP,
  KEYDOWN
}

Trait เหมือน Interface

trait Frobnicate<T> {
    fn frobnicate(self) -> Option<T>;
}

กำหนด function รับค่า return ค่า

fn sum(a: u32, b: u32) -> u32 {
  return a + b;
}

fn main() {
  let result = sum(10, 20);
  println!("10+20={}", result);
}

การ return ค่า ตอนจบ function จะใช้บรรทัดสุดท้าย (โดยไม่ต้องใส่ semicolon)

fn sum(a: u32, b: u32) -> u32 {
  a + b
}

// มีค่าเท่ากัน
fn sum(a: u32, b: u32) -> u32 {
  return a + b;
}

คร่าวๆ ก็มีประมาณนี้ สำหรับ Rust บทความแรก ก็ขอจบเพียงเท่านี้ก่อนครับ ส่วนตัวผมไม่กล้าแนะนำไปมากกว่านี้ เพราะยังไม่ชำนาญเท่าไหร่ กลัวจะสร้างความเข้าใจผิด 😅 แนะนำอ่านเพิ่มเติมครับ และหากใครมีอะไรแนะนำ ข้อเสนอแนะ มาพูดคุยกันได้นะครับ

  • Rustling และ Rust Book เหมาะสำหรับอ่านคู่กันมากครับ ( ตอนนี้ผมก็กำลังหัดอยู่ตรงนี้ครับ วนๆ บทที่ 1-10 หลายรอบมาก)
  • Tour of Rust - สามารถเข้าไปลองอ่าน ทำความเข้าใจได้ครับ
  • Rust by Example - คล้ายๆ Rust Book อ่านเพิ่มเติม ต่อยอดได้ครับ
  • Easy Rust - ผมชอบเล่มนี้ อ่านง่าย และเข้าใจง่ายดี (อ่านไปนิดเดียว)
  • Rust Learning - รวม links & resources ต่างๆ น่าจะมีครบหมด อยู่ที่ตัวเราแล้วแหละ

สรุป

หวังว่าบทความนี้จะเป็นประโยชน์ และเป็นแนวทาง หรือแรงบันดาลใจให้ใครหลายๆ คนที่กำลังสนใจ Rust มาลองหัดเขียน กันดูนะครับ แล้วมาเรียนไปพร้อมๆกันครับ

Happy Coding ❤️

Buy Me A Coffee
Authors
Discord