ทำไมต้อง Rust — คำสัญญาของ ownership กับ compiler ที่เป็นครู
ถ้าคุณมาจากสาย C#/.NET คุณมีเครื่องมือครบมืออยู่แล้ว — dotnet CLI, NuGet, MSBuild, GC ที่คอยเก็บขยะให้เอง และ exception ไว้จับความผิดพลาด Rust เสนอ ข้อตกลง คนละแบบ: มันขอให้คุณบอก compiler ให้ชัดว่า “ใครเป็นเจ้าของข้อมูลก้อนไหน” แล้วแลกกับความยุ่งยากนั้น มันให้สามอย่างที่ C# ให้พร้อมกันทั้งสามไม่ได้ — หน่วยความจำแบบ deterministic โดย ไม่มี GC, ไม่มี null, และไม่มี data race — ทั้งสามข้อ compiler บังคับตั้งแต่ตอน compile ไม่ใช่ปล่อยให้ไปพังเอาตอน runtime
หัวใจของทั้งคอร์สคือมุมมองเดียว: compiler ของ Rust คือครู ไม่ใช่อุปสรรค ช่วงแรกมันจะปฏิเสธ code ของคุณบ่อยจนน่าหงุดหงิด แต่ทุก error ที่มันโยนกลับมาคือ bug ที่มันกันไว้ให้ล่วงหน้า — double-free, dangling pointer, หรือ data race ที่ในโลก C# คุณจะไปเจอเอาตอน runtime (หรือไม่เจอเลยจนขึ้น production) เราจะย้ำมุมนี้ซ้ำทุกบท และในบทนี้เราแค่วางกรอบความคิด ติดตั้งเครื่องมือ แล้วสร้าง project แรก
code ลงมือของคอร์สนี้อยู่ใน repo kaen-taskcli (code ตัวอย่างกำลังจัดทำ) — เราสร้าง CLI ตัวเดียวชื่อ taskcli (ตัวจัดการงาน to-do ที่เก็บสถานะเป็น JSON) ไล่ยาว 8 บท ด้วยภาษาแกน + std + library ที่นิ่งจริงเพียงไม่กี่ตัว จงใจ ไม่แตะ async ทั้งกอง บทนี้เป็นบทแนวคิด + การตั้งค่าครั้งแรก — code รันได้จริงมีแค่ hello-world ที่ cargo สร้างให้ code feature จริงเริ่มที่บท2 บทนี้ตั้ง precedent ของ honesty spine กับ 🔗 callout อ้างอิงต้นทาง ให้ทุกบทหลังจากนี้เดินตาม
สามคำสัญญาที่ compiler บังคับให้ — และทำไมมันคือครู
หัวข้อที่มีชื่อว่า “สามคำสัญญาที่ compiler บังคับให้ — และทำไมมันคือครู”Rust สัญญาสามข้อ และทุกข้อคือ สะพาน จากสิ่งที่คุณรู้ใน C# แล้ว ข้ามไป ไม่ใช่ของอย่างเดียวกันเป๊ะๆ นี่คือกฎของคอร์สนี้ที่จะยึดตลอด: ทุกครั้งที่บอกว่า “เหมือน C# X” จะมี ”…ยกเว้น” ตามมาเสมอ
| สิ่งที่คุณรู้ใน C#/.NET | Rust ให้แทน | …ยกเว้น (สะพาน ไม่ใช่ของเดียวกัน) |
|---|---|---|
| GC เก็บทุก object ที่ยัง reachable แล้วคืนหน่วยความจำแบบไม่กำหนดเวลา | เจ้าของเดียว; ค่าถูก drop แบบ deterministic เมื่อเจ้าของหมด scope (RAII) | ไม่มี GC pause ไม่มี finalizer สุ่มจังหวะ — แต่คุณต้อง ตัดสินใจเองว่าใครเป็นเจ้าของอะไร คล้าย using/IDisposable แต่อัตโนมัติและครอบทุกอย่าง |
null + NullReferenceException โผล่ตอน runtime | Option<T> = Some(T) หรือ None | ”ไม่มีค่า” แทนไม่ได้เลยถ้าชนิดไม่ใช่ Option<T>; compiler บังคับ ให้จัดการกรณี None ก่อนใช้ค่า ไม่ใช่แค่ warning แบบ nullable-reference-type |
lock(obj) { … } เป็นแค่ธรรมเนียม ข้ามได้ ไม่มีใครกันการ share ข้าม thread | กฎ ownership/borrowing ทำให้ data race กลายเป็น compile error | เราจะเห็นเต็มๆ ในบท8 — ตรงนี้แค่รู้ว่า Rust พิสูจน์ ความปลอดภัยข้าม thread ตั้งแต่ compile ไม่ใช่หวังว่าคนเขียนจะใส่ lock ครบ |
สามแถวนี้คือ “ครู” ในภาคปฏิบัติ — คุณจ่ายค่าเล่าเรียนเป็น compile error ช่วงแรก แล้วได้คืนเป็น bug ทั้งชนชั้นที่หายไปจาก runtime ตลอดกาล ในบทนี้เราจะยังไม่ลงลึกกลไก (ownership เต็มๆ อยู่บท2) แค่รับปากไว้ว่านี่คือสิ่งที่เรากำลังจะแลก
std เท่านั้น — และทำไมเราจงใจข้าม async
หัวข้อที่มีชื่อว่า “std เท่านั้น — และทำไมเราจงใจข้าม async”เราสร้าง taskcli ด้วยภาษาแกน + std + library ที่นิ่งจริงไม่กี่ตัว (เช่น clap, serde, thiserror ในบทหลัง) และ จงใจข้าม async ecosystem ทั้งกอง (tokio, async-std, axum, reqwest) — concurrency ของคอร์สนี้คือ std::thread + std::sync ล้วนๆ
เหตุผล: async เกิดมาเพื่อ multiplex งาน I/O นับหมื่นงานบน thread ไม่กี่ตัว แต่ CLI ที่ทำงานปริมาณจำกัดต้องการ OS thread ตรงๆ มากกว่า — reflex “await มันสิ” ของ C# map มาที่ std::thread ไม่ใช่ Rust async ซึ่งเป็นโลก runtime แยกต่างหากที่ต้อง opt-in นี่ตรงกับ final project ของหนังสือ The Rust Programming Language เองที่เป็น web server แบบ multithread ไม่ใช่ async — เป็น design ของคอร์ส ไม่ใช่ช่องโหว่
เครื่องมือสามชิ้น: rustup, cargo, rustc
หัวข้อที่มีชื่อว่า “เครื่องมือสามชิ้น: rustup, cargo, rustc”ติดตั้ง Rust ครั้งเดียวผ่าน rustup (จาก rustup.rs) แล้วคุณจะได้เครื่องมือที่แบ่งงานกันชัดเจน:
rustup— ตัวจัดการ toolchain และ component: เพิ่ม/อัปเดต/สลับรุ่น stable, ติดตั้งclippyกับrustfmt, เพิ่ม target สำหรับ cross-compile- cargocargoเครื่องมือ build + package manager ของ Rust (รวม dotnet CLI + NuGet + MSBuild ไว้ในตัวเดียว) — build system + package manager ที่คุณจะใช้แทบทุกคำสั่งในแต่ละวัน
rustc— ตัว compiler จริงที่cargoเรียกให้อัตโนมัติ คุณแทบไม่เคยเรียกมันเองด้วยมือ- (เสริมแต่ควรมี)
rust-analyzer— LSP สำหรับ editor ให้ autocomplete และ inline error ตอนพิมพ์ ต่างจาก compile แล้วค่อยดู
ถ้าเทียบกับฝั่ง .NET จะจับภาพได้เร็วขึ้น — แต่ทุกแถวเป็นสะพาน ไม่ใช่ของเท่ากันเป๊ะ:
| Rust | เทียบกับฝั่ง .NET | จุดที่ต่าง |
|---|---|---|
rustup | การจัดการ version .NET SDK | Rust มี stable channel เดียวที่เร็ว และ ไม่มี LTS — คุณอยู่กับรุ่นล่าสุดเสมอ ต่างจากการ pin .NET 8 LTS ไว้หลายปี |
cargo | dotnet CLI + NuGet + MSBuild หลอมรวมเป็นตัวเดียว | คำสั่งเดียวคุมทั้ง build, test, dependency, publish; file Cargo.toml เล็กกว่า .csproj มาก |
rustc | csc/Roslyn ที่คุณไม่เคยเรียกด้วยมือ | cargo เรียกให้เองเบื้องหลัง |
cargo new | dotnet new console | ได้โครง project พร้อม git และ hello-world ในคำสั่งเดียว |
| crates.io | nuget.org | ทะเบียนกลางของ crate (ชื่อเรียก library ของ Rust) |
cargo new hello_cargo — เดินดู file ที่ถูกสร้าง
หัวข้อที่มีชื่อว่า “cargo new hello_cargo — เดินดู file ที่ถูกสร้าง”มาลองจริง สั่ง cargo new hello_cargo แล้ว cargo จะกางโครง project ให้:
$ cargo new hello_cargo Creating binary (application) `hello_cargo` packagenote: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.htmlสังเกตบรรทัด note: — cargo ชี้ไปที่หน้าคู่มือ manifest ให้ บนเทอร์มินัล (ใน rustc 1.97.1 คำอธิบายนี้พิมพ์เป็น note บนจอ ไม่ได้ฝังเป็น comment ลงใน file อีกต่อไป) file Cargo.toml ที่ได้จริง — นี่คือ manifest หัวใจของทุก project cargo — หน้าตาแบบนี้เป๊ะ:
[package]name = "hello_cargo"version = "0.1.0"edition = "2024"
[dependencies][dependencies] ว่างเปล่า — บทนี้ยัง ไม่มี dependency ใดๆ ทั้งสิ้น taskcli จะเริ่มเพิ่ม crate จริงในบทหลัง และ file src/main.rs:
fn main() { println!("Hello, world!");}สังเกตเครื่องหมาย ! ท้าย println! — นั่นบอกว่ามันเป็น macro ไม่ใช่ method หรือ function ธรรมดา macro ถูกกางออก (expand) เป็น code ตอน compile และรับ argument ได้ยืดหยุ่นกว่า function — เราจะเจอ println!, vec!, format! บ่อยตลอดคอร์ส จำ ! ไว้ว่า “นี่คือการเรียก macro” สั่ง cargo run เพื่อ compile แล้วรันในคำสั่งเดียว บรรทัดสุดท้ายที่ได้คือ Hello, world!
วงจรชั้นในของ cargo ที่ใช้ทุกวัน
หัวข้อที่มีชื่อว่า “วงจรชั้นในของ cargo ที่ใช้ทุกวัน”พอ project เริ่มโต คุณจะวนใช้คำสั่งชุดนี้ซ้ำๆ จนติดมือ:
cargo check— type-check + borrow-check ทั้ง project โดยไม่สร้าง binary เป็น feedback loop ที่เร็วที่สุด และ ไม่มีคู่เทียบตรงๆ ใน C# บน CLI (Roslyn มี analyzer ใน IDE แต่cargo checkเป็นขั้น CLI ที่ตั้งใจออกแบบมาให้เร็ว)cargo build/cargo build --release— สร้าง binary (--releaseเปิด optimization เต็มที่ แต่ compile ช้ากว่า)cargo run— build แล้วรันในทีเดียวcargo test— รัน test ทั้ง unit และ integrationcargo fmt— จัดรูปแบบ code ตามมาตรฐานกลาง (เลิกเถียงเรื่องสไตล์)cargo clippy— linter ที่จับ anti-pattern และเสนอสำนวนที่เป็น Rust มากขึ้น
ตามคู่มือ The Cargo Book คำสั่ง cargo check “type-checks the current package without producing a binary” — นั่นคือเหตุผลที่มันเร็วพอจะรันหลังพิมพ์ทุกไม่กี่วินาที ต่างจาก cargo build ที่ต้อง codegen + link จนได้ file รันจริง
flowchart TB
RU["rustup<br/>จัดการ toolchain + component"]
RU -->|"ติดตั้งให้"| RC["rustc<br/>ตัว compiler จริง"]
RU -->|"ติดตั้งให้"| CG["cargo<br/>build + package manager"]
RU -->|"ติดตั้งให้"| CF["clippy + rustfmt<br/>linter + ตัวจัดรูปแบบ"]
CG -->|"เรียกใช้ให้อัตโนมัติ"| RC
subgraph LOOP["วงจร cargo ที่ใช้ทุกวัน"]
direction LR
CK["cargo check<br/>type-check ไม่สร้าง binary"] --> BD["cargo build"] --> RN["cargo run"] --> TS["cargo test"]
end
CG --> LOOP
classDef tool fill:#fde68a,stroke:#92400e,color:#451a03;
classDef loop fill:#fed7aa,stroke:#7c2d12,color:#431407;
class RU,RC,CG,CF tool;
class CK,BD,RN,TS loop;
คำบรรยายภาพ: เครื่องมือของ Rust และวงจร cargo ที่ใช้ทุกวัน — rustup ติดตั้ง rustc, cargo, clippy และ rustfmt ให้; ในชีวิตประจำวันคุณคุยกับ cargo เป็นหลัก (ซึ่งเรียก rustc ให้เบื้องหลัง) แล้ววนอยู่ใน loop cargo check → build → run → test โดยเริ่มจาก check ที่เร็วที่สุดเพราะไม่ต้องสร้าง binary
edition ต่างจาก version compiler
หัวข้อที่มีชื่อว่า “edition ต่างจาก version compiler”มือใหม่มักสับสนสองเลขนี้ ทั้งที่มันคนละเรื่องกันสิ้นเชิง:
- editioneditionรุ่นภาษาแบบ opt-in (2015/2018/2021/2024) เลือกต่อ crate; ทุก edition ทำงานร่วมกันได้ (เช่น
edition = "2024"ใน Cargo.toml) คือ “ยุค” ของภาษาแบบ opt-in ออกราวทุก ~3 ปี (2015 → 2018 → 2021 → 2024) เลือกได้ ต่อ crate - version compiler (เช่น
rustc 1.97.1) คือตัว compiler เอง ออก stable ใหม่ทุก ~6 สัปดาห์แบบนาฬิกาปลุก
หนังสือ The Rust Programming Language (Appendix G) สรุปคาบการออกไว้ว่า “A new stable Rust is released every six weeks, like clockwork” และรองรับเฉพาะ stable ล่าสุดเท่านั้น ส่วน edition เป็นกลไกคนละแกน — ตาม Rust Edition Guide edition คือวิธี “ปล่อยการเปลี่ยนแปลงที่ break ความเข้ากันได้เล็กๆ แบบ opt-in โดย ไม่แตกระบบนิเวศ; crate ต่าง edition ทำงานร่วมกันได้อย่างราบรื่น” นั่นแปลว่า code edition 2015 เก่ายัง compile ด้วย rustc รุ่นล่าสุดได้ และคุณอัปเกรดทีละ crate ได้โดยไม่ต้องรอทั้ง project
edition 2024 นั้น ถูก stabilize ใน Rust 1.85.0 (2025-02-20) และเป็น edition ปริยายของ cargo new (ตาม block ประกาศ Rust 1.85.0) — นี่คือเหตุผลที่ Cargo.toml ของเราขึ้น edition = "2024" เอง ตรงนี้คือความต่างจาก .NET ที่มี LTS ให้ pin: Rust ไม่มี LTS แต่ edition คือสิ่งที่ทำให้การ “อยู่กับ stable ล่าสุดเสมอ” ไม่เจ็บ
ณ เวลาที่เขียนบทนี้ (as-of 2026-07-16) stable ล่าสุดคือ Rust 1.97.1 — แต่เพราะออกใหม่ทุก ~6 สัปดาห์ เลขนี้แทบจะขยับไปแล้วตอนคุณอ่าน ทั้งคอร์สนี้ pin version พร้อมวันที่กำกับเสมอ และคุณเช็กของจริงเองได้ตลอดด้วย rustc --version กับหน้า blog.rust-lang.org/releases — ตัวเลขที่ ไม่ ขยับคือ edition = "2024" เพราะ edition ผูกกับ crate ไม่ใช่กับรุ่น compiler
สรุปก่อนไปต่อ
หัวข้อที่มีชื่อว่า “สรุปก่อนไปต่อ”บทนี้วางกรอบว่าทำไมวิศวกร C#/.NET ถึงคุ้มที่จะข้ามมา Rust: หน่วยความจำ deterministic ไม่มี GC, ไม่มี null (Option<T>), ไม่มี data race — โดย compiler บังคับตั้งแต่ตอน compile และเราเลือกมอง compiler เป็น ครู ไม่ใช่อุปสรรค; เรารับปากเรื่อง std-only ที่จงใจข้าม async พร้อมเหตุผล; รู้จักเครื่องมือสามชิ้น rustup/cargo/rustc (+ rust-analyzer) และการ map มันกับ .NET toolchain; รัน cargo new hello_cargo เดินดู Cargo.toml กับ src/main.rs (จำ ! = macro); ผูกวงจร cargo check → build → run → test ไว้ในมือ; และแยก edition (ยุคภาษา, ~3 ปี, opt-in, ทำงานข้าม edition ได้) ออกจาก version compiler (1.97, ทุก 6 สัปดาห์) บทนี้ยังไม่มี code feature — มีแค่ hello-world ที่ cargo สร้างให้
บทหน้าเราลงของจริง: ownership, การยืม (borrowing) และการย้าย (move) — model หลักที่ทุกอย่างในคอร์สนี้ตั้งอยู่บนมัน เราจะเห็น compiler-ครูโยน error ตัวแรก (E0382) แล้วเข้าใจว่ามันกัน double-free ให้เราได้อย่างไร อย่ารีบผ่านบทนั้น — ทั้งคอร์สพักอยู่บนไอเดียเดียวนี้
บทนี้อิงต้นทางที่ลงวันที่กำกับ อ่านต่อได้โดยตรง:
- The Rust Programming Language — ch01-03 “Hello, Cargo!” (เข้าถึง 2026-07-23) — ที่มาของ
cargo new, โครงCargo.toml+src/main.rs, และcargo build/run/check - The Rust Programming Language — Appendix G: How Rust is Made and “Nightly Rust” (เข้าถึง 2026-07-23) — คาบการออก stable ทุก 6 สัปดาห์ (train model) และรองรับเฉพาะ stable ล่าสุด
- The Cargo Book — cargo check (commands reference) (เข้าถึง 2026-07-23) —
cargo checktype-check โดยไม่สร้าง binary - Rust Edition Guide — What are editions? (เข้าถึง 2026-07-23) — edition เป็นกลไก opt-in ที่ไม่แตกระบบนิเวศ และ crate ต่าง edition ทำงานร่วมกันได้
- Announcing Rust 1.85.0 and Rust 2024 (2025-02-20) — edition 2024 ถูก stabilize ที่นี่ และเป็น edition ปริยายของ
cargo new - Announcing Rust 1.97.1 (2026-07-16) — stable ล่าสุด ณ เวลาที่เขียน (ตัวเลขนี้ขยับทุก ~6 สัปดาห์ — เช็ก
rustc --versionเสมอ)
เช็กความเข้าใจ — บทที่ 1
ข้อ 1 / 3`cargo check` ทำอะไร และทำไมมันถึงเป็นคำสั่งที่ใช้บ่อยในวงจรชั้นใน?