← Back to notes
TECHNICAL FIELD NOTES

Rust 101: Essential Concepts for Absolute Beginners

พื้นฐาน Rust สำหรับมือใหม่ ตั้งแต่ตัวแปร ชนิดข้อมูลและ control flow ไปจนถึง error handling, modules, structs และเครื่องมือที่ใช้พัฒนา

Previously published at Original on the previous platform ↗ · First published

หมายเหตุการย้ายบทความ: ตัวอย่างในหัวข้อ 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 mut keyword 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 const keyword
  • 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 let keyword each time (ใช้คำสั่ง let ทุกครั้ง ถ้าอยากจะทำ shadowing)
  • Different from mut as 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 fn keyword
  • 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-else expressions 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 interaction
  • break: 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 (ใช้ ? เพื่อส่งต่อข้อผิดพลาด)
  • match and if let for 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 mod keyword to define a module ( mod ใช้เพื่อสร้างโมดูล)
  • pub keyword makes items public (ใช้ pub เพื่อทำให้ไอเท็มเข้าถึงได้จากภายนอก)
    • use keyword brings items into scope (ใช้ use เพื่อนำไอเท็มมาใช้ในขอบเขตปัจจุบัน)
    • Crates are packages of Rust code
    • Cargo is 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 String
  • to_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 file
  • File::create() to create a new file
  • write! 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 (ชนิดข้อมูลที่มีค่าที่เป็นไปได้จำกัด)
  • impl keyword for implementing methods (ใช้ impl เพื่อเพิ่มเมธอดให้ struct หรือ enum)
  • Self refers to the type in implementations (Self อ้างอิงถึงชนิดข้อมูลในการ implement)
  • Enums can 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

  • match expression for exhaustive pattern matching (ใช้ match เพื่อจับคู่รูปแบบอย่างครอบคลุม)
  • _ wildcard pattern matches any value (ใช้ _ เพื่อจับคู่กับค่าใดๆ)
  • if let for 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 compiler
  • cargo: Rust’s package manager and build tool
  • rustup: 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.tom l: 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 🤤🖥🤗

Read more notes →