หมายเหตุการย้ายบทความ: ตัวอย่างในหัวข้อ Modules and Packages ขาดวงเล็บปีกกาปิดของ
mod mathและตัวอย่าง Structs and Enums ขาดวงเล็บปีกกาปิดของimpl Personใน clipping ต้นฉบับ จึงคงโค้ดไว้โดยไม่เติมคำสั่งที่ไม่มีในต้นฉบับ ตัวอย่างยังไม่ได้ผ่านการคอมไพล์หรือทดสอบในการย้ายครั้งนี้

Photo by Kai Dahms on Unsplash
สวัสดีครับทุกคน เจอกันอีกครั้งในโหมดสาระของผมกับบทความ Rust บทความแรกซึ่งต้องขอบอกก่อนว่า Blog นี้เป็นหนึ่งใน “บาป” ที่ค้างอยู่ใน Drafts ของผม ที่เคยเขียนๆทิ้งไว้ใน Medium เมื่อปีสองปีที่แล้วตอนที่เริ่มสนใจ Rust ครับ
ชื่อเดิมจะเป็น Rust: Common Programming Concepts เพราะตอนแรกผมเน้นทำเป็นโน๊ตๆไว้ ตอนเตรียมสอนน้องที่รู้จักกันที่กำลังเรียน Rust ในมหาวิทยาลัยตอนปี 1 ผมเองก็ถือว่าได้เรียนไปพร้อมน้องด้วยเลยตอนนั้น รู้สึกดีมากๆด้วยเพราะน้องได้เริ่ม Rust ตั้งแต่ปี 1 เลย (จำได้ว่าน้องเรียน Python ด้วย ผมเลยได้อานิสงค์สอนทั้ง Rust + Python ไปด้วยเลย ซึ่งตอนนี้น้องน่าจะเทพกว่าผมไปเรียบร้อยแล้ว… ฮา)
รูปแบบใน Blog นี้เลยจะเป็นกึ่งสรุปสั้นๆ ใส่ bullets ไว้ จะได้อ่านผ่านๆตาแบบง่ายๆ เนื้อหาจะเน้นไปที่ Basic ก่อนไปลงลึกในตอนที่เราเขียนเป็นตัวโปรแกรมหรือแก้โจทย์กันจริงๆนะครับ
บทความนี้เลยจะพาหลบเรื่องที่ซับซ้อนอย่าง Ownership หรือหัวข้ออื่นๆอย่างเช่น Lifetimes, Traits, Generics, Concurrency, Parallelism และ บลาๆทั้งหลาย เราเลยจะมาเริ่มต้นง่ายๆกันก่อน แล้วค่อยไปเรื่องยากๆกันทีหลังครับ มือใหม่ไม่ต้องกังวลครับผม ก่อนไปลุยเรื่องปวดหัวที่อื่น เรามาลุยพื้นฐานกันที่นี่ก่อนเลยครับ 🤣
Topics
- Variables
- Constants
- Shadowing
- Data Types
- Functions
- Control Flow
- Comments
- Error Handling
- Modules and Packages
- Basic String Manipulation
- Simple Input/Output
- Structs and Enums
- Pattern Matching
- Rust Toolchain
Variables
- Variables in Rust are immutable by default (ตัวแปรใน Rust เป็นแบบเปลี่ยนแปลงค่าไม่ได้ตั้งแต่เริ่มต้น)
- Use
mutkeyword to make a variable mutable (อยากให้เปลี่ยนค่าได้ก็ใส่mutเข้าไป) - Immutability helps keep code safe and predictable
- Mutable variables offer flexibility when needed
- Type inference allows Rust to guess the type in most cases (Rust สามารถเดาชนิดข้อมูลได้)
// Example
let x = 5; // Immutable (เปลี่ยนแปลงค่าไม่ได้)
let mut y = 5; // Mutable (เปลี่ยนแปลงค่าได้)
let z: i32 = 10; // Explicit type annotation (ระบุชนิดข้อมูลชัดเจน)
Constants
- Constants are always immutable (เป็นค่าคงที่ เปลี่ยนแปลงไม่ได้)
- Declared using the
constkeyword - Must be type annotated (ต้องระบุชนิดข้อมูลเสมอ)
- Can be declared in any scope, including global scope (สามารถประกาศได้ในทุกขอบเขต รวมถึงขอบเขตทั่วไป)
- Naming convention: all uppercase with underscores (ใช้ตัวพิมพ์ใหญ่ทั้งหมดและขีดล่างในการตั้งชื่อ จะได้สังเกตุง่าย)
- Value must be a constant expression, not the result of a function call or any other value that could only be computed at runtime
// Example
const PI: f64 = 3.14159265358979323846264338327950288;
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
const MAX_POINTS: u32 = 100_000;
Shadowing
- Allows reuse of variable names (อนุญาตให้ใช้ชื่อตัวแปรซ้ำได้)
- Creates a new variable while reusing the same name (สร้างตัวแปรใหม่โดยใช้ชื่อเดิม)
- Useful for changing variable type without creating a new name (มีประโยชน์ในการเปลี่ยนชนิดข้อมูลโดยไม่ต้องสร้างชื่อใหม่)
- Uses the
letkeyword each time (ใช้คำสั่งletทุกครั้ง ถ้าอยากจะทำshadowing) - Different from
mutas it creates a new variable each time (ต่างจากmutตรงที่mutจะสร้างตัวแปรใหม่ทุกครั้ง) - Shadowing respects scopes, allowing for more flexible use of variable names
// Example#1
let x = 5;
let x = x + 1; // x is now 6 (x ตอนนี้เป็น 6)
let x = x * 2; // x is now 12 (x ตอนนี้เป็น 12)
// Emaple#2
fn main() {
let x = 5;
let x = x + 1; // x = 6
{
let x = x * 2; // x = 12
println!("Inside block: x = {}", x);
}
println!("Outside block: x = {}", x); // x = 6
}
// Example#3: Changing types
let spaces = " ";
let spaces = spaces.len(); // Now spaces is a number
Data Types
- Rust is statically typed (Rust ต้องระบุชนิดข้อมูลตั้งแต่ตอนคอมไพล์)
- The compiler can usually infer types
(คอมไพเลอร์สามารถเดาชนิดข้อมูลได้ส่วนใหญ่) - Two main categories: Scalar and Compound
(มีสองประเภทหลัก: ชนิดข้อมูลพื้นฐานและชนิดข้อมูลประกอบ)
Scalar Types:
- Integers: i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
- Floating-point: f32, f64
- Boolean: bool (true or false)
- Character: char (represents a Unicode Scalar Value)
(ตัวอักษร: แทนค่า Unicode หนึ่งตัว, emoji แบบนี้ก็ได้นะ “🦀”)
Compound Types:
- Tuples: Fixed-length collection of various types (ชุดข้อมูลความยาวคงที่ ที่มีชนิดข้อมูลต่างกันได้)
- Arrays: Fixed-length collection of same type (ชุดข้อมูลความยาวคงที่ ที่มีชนิดข้อมูลเดียวกัน)
// Tuple
let tup = (500, 6.4, true);
let (x, y, z) = tup; // Destructuring (เอามาใช้แบบนี้ได้)
// Array
let arr = [1, 2, 3, 4, 5];
Functions
- Defined using the
fnkeyword - Can take parameters and return values (รับพารามิเตอร์และส่งค่ากลับได้)
- Return value is the last expression in the function (without semicolon (ค่าที่ส่งกลับไม่ต้องใส่ “;” (semicolon))
- Parameters must have type annotations
- Function bodies can contain statements and expressions
- Function calls:
let lunch = make_sandwich("peanut butter");
// Example#1
fn make_sandwich(ingredient: &str) -> String {
format!("Here's your delicious {} sandwich!", ingredient)
}
let lunch = make_sandwich("peanut butter");
println!("{}", lunch); // Here's your delicious peanut butter sandwich!
// Example#2
fn is_divisible_by(dividend: u32, divisor: u32) -> bool {
if divisor == 0 {
return false; // Early return for invalid input
}
dividend % divisor == 0
}
Control Flow
if-elseexpressions for conditional code execution- Loops for repeated execution:
loop,while,for loop: Infinite loop unless broken (วนซ้ำไม่สิ้นสุดจนกว่าจะสั่งbreak)while: Conditional loop (วนซ้ำตามเงื่อนไข)for: Iterating over collections (วนซ้ำผ่านคอลเลกชัน)continue: Skip the rest of interactionbreak: Exit the loop
// Example
if mood == "happy" {
println!("Let's dance! 💃");
} else if mood == "hungry" {
println!("Time for a coding snack! 🍕");
} else {
println!("Let's write some Rust and chill 😎");
}
// For loop
for day in ["Monday", "Tuesday", "Wednesday"].iter() {
println!("Today is {}, let's code!", day);
}
// While loop
let mut number = 3;
while number != 0 {
println!("{}!", number);
number -= 1;
}
// Loop with a return value
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
Comments
- Single-line comments: Start with
//(คอมเมนต์บรรทัดเดียว) - Multi-line comments: Enclosed in
/* */(คอมเมนต์หลายบรรทัด) - Documentation comments: Start with
///or//!(คอมเมนต์สำหรับสร้างเอกสาร) - Use comments to explain why, not what (ใช้คอมเมนต์เพื่ออธิบายเหตุผล ไม่ใช่อธิบายว่าโค้ดทำอะไร)
// This is a single-line comment
/* This is a multi-line comment
It can span several lines */
/// This generates documentation for the following item
Error Handling
- Result and Option types for handling errors and optional values (Result และ Option ใช้จัดการข้อผิดพลาดและค่าที่อาจมีหรือไม่มี)
Result<T, E>: T for success value, E for error value (T สำหรับค่าที่ถูกต้อง, E สำหรับค่าข้อผิดพลาด)Option<T>: Some(T) for a value, None for no value (Some(T) เมื่อมีค่า, None เมื่อไม่มีค่า)?operator for error propagation (ใช้?เพื่อส่งต่อข้อผิดพลาด)matchandif letfor handling different cases (ใช้matchและif letในการจัดการกรณีต่างๆ)
// Example: Result
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err("Cannot divide by zero!".to_string())
} else {
Ok(a / b)
}
}
// Example: Option
fn find_even(numbers: &[i32]) -> Option<i32> {
numbers.iter().find(|&&x| x % 2 == 0).copied()
}
// Example: ? operator
fn read_username() -> Result<String, io::Error> {
let mut file = File::open("username.txt")?;
let mut username = String::new();
file.read_to_string(&mut username)?;
Ok(username)
}
// Example: match and if let
match divide(10.0, 2.0) {
Ok(result) => println!("Result: {}", result),
Err(e) => println!("Error: {}", e),
}
if let Some(even) = find_even(&[1, 3, 5, 6, 7]) {
println!("Found even number: {}", even);
}
Modules and Packages
- Modules organize code into namespaces (โมดูลช่วยจัดระเบียบโค้ดเป็นหมวดหมู่)
- Use
modkeyword to define a module (modใช้เพื่อสร้างโมดูล) pubkeyword makes items public (ใช้pubเพื่อทำให้ไอเท็มเข้าถึงได้จากภายนอก)
•usekeyword brings items into scope (ใช้ use เพื่อนำไอเท็มมาใช้ในขอบเขตปัจจุบัน)
•Cratesare packages of Rust code
•Cargois Rust’s package manager (Cargo เป็นตัวจัดการแพ็คเกจของ Rust)
// Example: Defining a module
mod math {
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
// Example: Using a module
use math::add;
fn main() {
println!("5 + 3 = {}", add(5, 3));
}
// Example: Using an external crate
// In Cargo.toml:
// [dependencies]
// rand = "0.8.5"
use rand::Rng;
fn main() {
let random_number = rand::thread_rng().gen_range(1..101);
println!("Random number: {}", random_number);
}
Basic String Manipulation
String: owned, growable UTF-8 string (สตริงที่ปรับขนาดได้ เป็น UTF-8)&str: string slice, view into Stringto_string()converts &str to String (แปลง &str เป็น String)&operator borrows String as &str (ใช้&เพื่อยืม String เป็น &str)+operator concatenates String and &str (ใช้+เพื่อต่อStringกับ&str)format!macro for complex string formatting (ใช้format!สำหรับการจัดรูปแบบสตริงที่ซับซ้อน)
// Example: String and &str
let s: String = String::from("hello");
let slice: &str = &s[0..2];
// Example: Conversion
let s: String = "hello".to_string();
let slice: &str = &s;
// Example: Concatenation
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // s1 is moved here and can no longer be used
// Example: String formatting
let name = "Alice";
let age = 30;
let formatted = format!("My name is {} and I'm {} years old.", name, age);
Simple Input/Output
println!macro for printing to console (ใช้println!เพื่อแสดงผลที่คอนโซล)std::io::stdin().read_line()for reading user input (ใช้อ่านข้อมูลจากผู้ใช้)File::open()to open a fileFile::create()to create a new filewrite!macro for writing to files?operator for error handling in I/O operations (จัดการข้อผิดพลาดในการทำ I/O)
// Example: Reading user input
use std::io;
fn main() {
println!("Enter your name:");
let mut input = String::new();
io::stdin().read_line(&mut input).expect("Failed to read line");
println!("Hello, {}!", input.trim());
}
// Example: File I/O
use std::fs::File;
use std::io::{Write, Read};
fn main() -> std::io::Result<()> {
// Writing to a file
let mut file = File::create("example.txt")?;
write!(file, "Hello, Rust!")?;
// Reading from a file
let mut content = String::new();
File::open("example.txt")?.read_to_string(&mut content)?;
println!("File content: {}", content);
Ok(())
}
Structs and Enums
Structs: custom data types to group related data (โครงสร้างข้อมูลที่รวมข้อมูลที่เกี่ยวข้องกัน)Enums: types with a fixed set of variants (ชนิดข้อมูลที่มีค่าที่เป็นไปได้จำกัด)implkeyword for implementing methods (ใช้implเพื่อเพิ่มเมธอดให้ struct หรือ enum)Selfrefers to the type in implementations (Selfอ้างอิงถึงชนิดข้อมูลในการ implement)Enumscan hold data in their variants (Enumสามารถเก็บข้อมูลในแต่ละ variant ได้)
// Example: Struct
struct Person {
name: String,
age: u32,
}
impl Person {
fn new(name: String, age: u32) -> Self {
Person { name, age }
}
fn introduce(&self) {
println!("Hi, I'm {} and I'm {} years old.", self.name, self.age);
}
// Example: Enum
enum TrafficLight {
Red,
Yellow,
Green,
}
impl TrafficLight {
fn is_stop(&self) -> bool {
matches!(self, TrafficLight::Red)
}
}
// Usage:
fn main() {
let alice = Person::new("Alice".to_string(), 30);
alice.introduce();
let light = TrafficLight::Red;
println!("Should stop: {}", light.is_stop());
}
Pattern Matching
matchexpression for exhaustive pattern matching (ใช้matchเพื่อจับคู่รูปแบบอย่างครอบคลุม)_wildcard pattern matches any value (ใช้_เพื่อจับคู่กับค่าใดๆ)if letfor matching a single pattern (ใช้if letเมื่อต้องการจับคู่เพียงรูปแบบเดียว)@binds a name to a matched value (ใช้@เพื่อผูกชื่อกับค่าที่จับคู่ได้)..ignores remaining parts of a value (ใช้..เพื่อละเว้นส่วนที่เหลือของค่า)
// Example: match with enum
enum Coin {
Penny,
Nickel,
Dime,
Quarter(String),
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("State quarter from {}!", state);
25
}
}
}
// Example: if let
let some_value = Some(3);
if let Some(3) = some_value {
println!("three");
}
// Example: @ binding
let x = 1;
match x {
e @ 1..=5 => println!("got a range element {}", e),
_ => println!("anything"),
}
// Example: .. to ignore
struct Point {
x: i32,
y: i32,
z: i32,
}
let origin = Point { x: 0, y: 0, z: 0 };
match origin {
Point { x, .. } => println!("x is {}", x),
}
Rust Toolchain
rustc: Rust compilercargo: Rust’s package manager and build toolrustup: Rust toolchain installer and version manager (ตัวติดตั้งและจัดการเวอร์ชันของ Rust)cargo new: Creates a new Rust project (สร้างโปรเจค Rust ใหม่)cargo build: Compiles the current project (เอาไว้ Compile)cargo run: Builds and runs the current project (สร้างและรันโปรเจคปัจจุบัน)cargo test: Runs the tests in the project (ใช้รันเทส)Cargo.toml: Project configuration file (ไฟล์เก็บพวกค่า config ต่างๆ)
Conclusion
สำหรับโน๊ตที่ผมทำไว้ในการเริ่มต้นเรียน Rust ก็คงจะมีประมาณนี้ครับ ผิดถูกตรงไหนอย่าลืมคอมเม้นมาบอกกันด้วยนะครับ 🙏😅
ใครอยากหารายละเอียดเพิ่มเติมก็สามารถเข้าไปดูได้ที่ website rust-lang ได้เลยครับ
- https://www.rust-lang.org/learn
- https://doc.rust-lang.org/rust-by-example/ (อันนี้ก็อ่านง่ายครับ)
- Rust Online Playground (อันนี้เป็น Online compiler เผื่ออยากไปลองฝึกเขียนกันเล่นๆครับ)
- https://book.gist.rs/ อันนี้แนะนำมากๆอีกอัน พึ่งไปเจอมาของพี่ต๊อปเอง katopz มีพวก runnable code snippet อ่านไปเล่นไป เข้าใจไวและง่ายขึ้นกว่าเดิมด้วยครับ
ตอนผมเริ่มก็อ้างอิงจากลิงก์ด้านบนเป็นหลักครับ ซึ่งเนื้อหาจะมีเยอะกว่านี้มากครับ สำหรับคนที่สนใจก็อย่าลืมไปลุยกันต่อนะครับ
อยากให้ทุกคนสนุกกับการเขียนโค้ดครับ
Be Lazy & Happy Coding 🤤🖥🤗