19 เมษายน 2568

บทสอน Rust: คู่มือเริ่มต้น

Rust ได้รับความสนใจจากนักพัฒนาอย่างต่อเนื่อง โดยครองตำแหน่งภาษาโปรแกรมที่ “เป็นที่รักมากที่สุด” ในการสำรวจของ Stack Overflow ติดต่อกันหลายปี นี่ไม่ใช่แค่กระแส Rust นำเสนอการผสมผสานที่น่าสนใจระหว่างประสิทธิภาพ ความปลอดภัย และคุณสมบัติภาษาที่ทันสมัย ซึ่งช่วยแก้ปัญหาทั่วไปที่พบในภาษาโปรแกรมเชิงระบบอื่นๆ หากคุณสงสัยว่าอะไรทำให้ Rust พิเศษและต้องการเริ่มต้นการเดินทาง คู่มือสำหรับผู้เริ่มต้นนี้จะให้ความรู้พื้นฐานเพื่อให้คุณเริ่มต้นได้ เราจะสำรวจไวยากรณ์หลัก แนวคิดเฉพาะตัว เช่น ความเป็นเจ้าของ (ownership) และเครื่องมือที่จำเป็นซึ่งขับเคลื่อนระบบนิเวศของ Rust

ทำไมต้องเรียนการเขียนโปรแกรม Rust?

Rust วางตำแหน่งตัวเองเป็นภาษาสำหรับการสร้างซอฟต์แวร์ที่เชื่อถือได้และมีประสิทธิภาพ ข้อได้เปรียบหลักอยู่ที่ความปลอดภัยของหน่วยความจำโดยไม่ต้องอาศัย garbage collector และการเปิดใช้งานการทำงานพร้อมกันที่ปลอดภัยไร้กังวล (fearless concurrency) นี่คือเหตุผลที่คุณควรพิจารณาเรียนรู้ Rust:

  1. ประสิทธิภาพ (Performance): Rust คอมไพล์โดยตรงไปยังรหัสเครื่อง (machine code) ทำให้มีประสิทธิภาพเทียบเท่ากับ C และ C++ มันบรรลุความเร็วนี้โดยไม่สูญเสียความปลอดภัย ทำให้เหมาะสำหรับแอปพลิเคชันที่ต้องการประสิทธิภาพสูง เช่น เอนจิ้นเกม ระบบปฏิบัติการ ส่วนประกอบเบราว์เซอร์ และเว็บเซอร์วิสประสิทธิภาพสูง
  2. ความปลอดภัยของหน่วยความจำ (Memory Safety): คุณสมบัติเด่นของ Rust คือ ระบบความเป็นเจ้าของ (ownership system) ซึ่งเสริมด้วย การยืม (borrowing) และ ช่วงชีวิต (lifetimes) ระบบนี้รับประกันความปลอดภัยของหน่วยความจำ ณ เวลาคอมไพล์ (compile time) ลืมเรื่อง dangling pointers, buffer overflows หรือ data races ที่มักเป็นปัญหาในภาษาอย่าง C/C++ ไปได้เลย คอมไพเลอร์ Rust ทำหน้าที่เป็นผู้เฝ้าประตูที่เข้มงวด ป้องกันข้อผิดพลาดทั่วไปเหล่านี้ก่อนที่โค้ดของคุณจะทำงานเสียอีก
  3. การทำงานพร้อมกัน (Concurrency): การเขียนโปรแกรมแบบทำงานพร้อมกัน (การทำงานหลายอย่างพร้อมกันเสมือนเกิดขึ้นในเวลาเดียวกัน) เป็นเรื่องที่ยากอย่างฉาวโฉ่ที่จะทำให้ถูกต้อง Rust จัดการปัญหานี้อย่างตรงไปตรงมา ระบบความเป็นเจ้าของและระบบประเภทข้อมูลทำงานร่วมกันเพื่อป้องกัน data races ณ เวลาคอมไพล์ ทำให้การเขียนแอปพลิเคชันแบบหลายเธรด (multi-threaded) ง่ายขึ้นและปลอดภัยขึ้นอย่างมาก “การทำงานพร้อมกันที่ปลอดภัยไร้กังวล” นี้ช่วยให้นักพัฒนาสามารถใช้ประโยชน์จากโปรเซสเซอร์แบบหลายคอร์สมัยใหม่ได้อย่างมีประสิทธิภาพโดยไม่มีข้อผิดพลาดทั่วไป
  4. เครื่องมือที่ทันสมัย (Modern Tooling): Rust มาพร้อมกับ Cargo ซึ่งเป็นตัวจัดการแพ็กเกจและเครื่องมือสร้าง (build tool) ที่ยอดเยี่ยมซึ่งรวมอยู่ในประสบการณ์หลัก Cargo จัดการการพึ่งพา (dependency management), การทดสอบ, การสร้าง, และการเผยแพร่ เครต (crates) (คำศัพท์ของ Rust สำหรับแพ็กเกจหรือไลบรารี) ได้อย่างราบรื่น ทำให้กระบวนการพัฒนาทั้งหมดคล่องตัวขึ้น
  5. ระบบนิเวศและชุมชนที่เติบโต (Growing Ecosystem & Community): ชุมชน Rust เป็นที่รู้จักว่ามีชีวิตชีวา กระตือรือร้น และยินดีต้อนรับผู้มาใหม่ ระบบนิเวศของไลบรารี (เครต) กำลังขยายตัวอย่างรวดเร็ว ครอบคลุมหลากหลายด้านตั้งแต่การพัฒนาเว็บ (เฟรมเวิร์กเช่น Actix, Rocket, Axum) และเครือข่าย (เช่น Tokio สำหรับการดำเนินการแบบอะซิงโครนัส) ไปจนถึงระบบฝังตัว (embedded systems) วิทยาศาสตร์ข้อมูล (data science) และเครื่องมือบรรทัดคำสั่ง (command-line tools)

เรียนเขียนโปรแกรม Rust: เครื่องมือที่จำเป็น

ก่อนที่คุณจะเขียนโค้ด Rust บรรทัดแรก คุณต้องตั้งค่าชุดเครื่องมือ Rust (Rust toolchain) วิธีมาตรฐานและแนะนำในการติดตั้ง Rust คือการใช้ rustup ซึ่งเป็นตัวติดตั้งชุดเครื่องมือ Rust

  1. rustup: เครื่องมือบรรทัดคำสั่งนี้จัดการการติดตั้ง Rust ของคุณ ช่วยให้คุณสามารถติดตั้ง อัปเดต และสลับระหว่าง Rust เวอร์ชันต่างๆ (เช่น stable, beta หรือ nightly builds) ได้อย่างง่ายดาย ไปที่เว็บไซต์ทางการของ Rust (https://www.rust-lang.org/tools/install) สำหรับคำแนะนำในการติดตั้งเฉพาะสำหรับระบบปฏิบัติการของคุณ
  2. rustc: นี่คือคอมไพเลอร์ Rust หลังจากที่คุณเขียนซอร์สโค้ด Rust ในไฟล์ .rs แล้ว rustc จะคอมไพล์ไฟล์เหล่านั้นเป็นไฟล์ไบนารีที่เรียกใช้งานได้ (executable binaries) หรือไลบรารีที่คอมพิวเตอร์ของคุณเข้าใจได้ แม้ว่าจะสำคัญ แต่โดยปกติคุณจะไม่เรียกใช้ rustc โดยตรงบ่อยนักในขั้นตอนการทำงานประจำวันของคุณ
  3. cargo: นี่คือระบบการสร้างและตัวจัดการแพ็กเกจของ Rust และเป็นเครื่องมือที่คุณจะโต้ตอบด้วยบ่อยที่สุด Cargo จัดการงานพัฒนาทั่วไปหลายอย่าง:
    • สร้างโปรเจกต์ใหม่ (cargo new)
    • สร้างโปรเจกต์ของคุณ (cargo build)
    • รันโปรเจกต์ของคุณ (cargo run)
    • รันการทดสอบอัตโนมัติ (cargo test)
    • จัดการการพึ่งพา (ดึงและคอมไพล์ไลบรารีภายนอก หรือเครต ที่ระบุไว้ในไฟล์ Cargo.toml ของคุณโดยอัตโนมัติ)
    • เผยแพร่เครตของคุณเองไปยัง crates.io (ทะเบียนแพ็กเกจ Rust ส่วนกลาง) เพื่อให้ผู้อื่นใช้งาน

สำหรับการทดลองอย่างรวดเร็วโดยไม่ต้องติดตั้งในเครื่อง แพลตฟอร์มออนไลน์ เช่น Rust Playground อย่างเป็นทางการ หรือสภาพแวดล้อมการพัฒนาแบบรวม เช่น Replit เป็นตัวเลือกที่ยอดเยี่ยม

โปรแกรม Rust แรกของคุณ: สวัสดี Rust!

มาเริ่มกันด้วยโปรแกรม “Hello, world!” แบบดั้งเดิม ซึ่งเป็นพิธีการสำหรับการเรียนรู้ภาษาใหม่ สร้างไฟล์ชื่อ main.rs และเพิ่มโค้ดต่อไปนี้:

fn main() {
    // บรรทัดนี้พิมพ์ข้อความไปยังคอนโซล
    println!("Hello, Rust!");
}

ในการคอมไพล์และรันโปรแกรมง่ายๆ นี้โดยใช้เครื่องมือพื้นฐาน:

  1. คอมไพล์: เปิดเทอร์มินัลหรือ command prompt ของคุณ ไปยังไดเรกทอรีที่มีไฟล์ main.rs และรัน:
    rustc main.rs
    
    คำสั่งนี้เรียกใช้คอมไพเลอร์ Rust (rustc) ซึ่งสร้างไฟล์ที่เรียกใช้งานได้ (เช่น main บน Linux/macOS, main.exe บน Windows)
  2. รัน: เรียกใช้โปรแกรมที่คอมไพล์แล้วจากเทอร์มินัลของคุณ:
    ./main
    # บน Windows ใช้: .\main.exe
    

อย่างไรก็ตาม สำหรับอะไรก็ตามที่นอกเหนือจากไฟล์เดียว การใช้ Cargo เป็นวิธีมาตรฐานและสะดวกกว่ามาก:

  1. สร้างโปรเจกต์ Cargo ใหม่: ในเทอร์มินัลของคุณ รัน:
    cargo new hello_rust
    cd hello_rust
    
    Cargo สร้างไดเรกทอรีใหม่ชื่อ hello_rust ซึ่งมีไดเรกทอรีย่อย src พร้อมไฟล์ main.rs (มีโค้ด “Hello, Rust!” อยู่แล้ว) และไฟล์กำหนดค่าชื่อ Cargo.toml
  2. รันด้วย Cargo: เพียงแค่รัน:
    cargo run
    
    Cargo จะจัดการขั้นตอนการคอมไพล์แล้วจึงเรียกใช้โปรแกรมผลลัพธ์ โดยแสดง “Hello, Rust!” บนคอนโซลของคุณ

มาดูรายละเอียดโค้ดกัน:

  • fn main(): นี่คือการกำหนดฟังก์ชันหลัก คำหลัก fn หมายถึงการประกาศฟังก์ชัน main เป็นชื่อฟังก์ชันพิเศษ เป็นจุดเริ่มต้นที่โปรแกรม Rust ที่เรียกใช้งานได้ทุกโปรแกรมเริ่มทำงาน วงเล็บ () บ่งชี้ว่าฟังก์ชันนี้ไม่รับพารามิเตอร์อินพุต
  • {}: วงเล็บปีกกา กำหนดบล็อกโค้ดหรือ ขอบเขต (scope) โค้ดทั้งหมดที่เป็นของฟังก์ชันจะอยู่ภายในวงเล็บปีกกาเหล่านี้
  • println!("Hello, Rust!");: บรรทัดนี้ทำหน้าที่พิมพ์ข้อความไปยังคอนโซล
    • println! เป็น มาโคร (macro) ของ Rust มาโครคล้ายกับฟังก์ชัน แต่มีความแตกต่างที่สำคัญคือ ลงท้ายด้วยเครื่องหมายอัศเจรีย์ ! มาโครทำการสร้างโค้ด ณ เวลาคอมไพล์ ทำให้มีพลังและความยืดหยุ่นมากกว่าฟังก์ชันปกติ (เช่น การจัดการอาร์กิวเมนต์จำนวนไม่แน่นอน ซึ่ง println! ทำได้) มาโคร println! พิมพ์ข้อความที่ให้มาไปยังคอนโซลและเพิ่มอักขระขึ้นบรรทัดใหม่ที่ส่วนท้ายโดยอัตโนมัติ
    • "Hello, Rust!" คือ สตริงลิเทอรัล (string literal) – ลำดับอักขระคงที่ที่แสดงถึงข้อความ อยู่ในเครื่องหมายคำพูดคู่
    • ;: เครื่องหมายอัฒภาค แสดงถึงจุดสิ้นสุดของคำสั่ง (statement) โค้ด Rust ที่ปฏิบัติการได้ส่วนใหญ่ (คำสั่ง) จะลงท้ายด้วยเครื่องหมายอัฒภาค

บทเรียน Rust สำหรับผู้เริ่มต้น: แนวคิดหลัก

ตอนนี้ มาเจาะลึกองค์ประกอบพื้นฐานของภาษาโปรแกรม Rust กัน

ตัวแปรและการเปลี่ยนแปลงค่าได้ (Variables and Mutability)

ตัวแปรใช้เพื่อเก็บค่าข้อมูล ใน Rust คุณประกาศตัวแปรโดยใช้คำหลัก let

let apples = 5;
let message = "Take five";

แนวคิดหลักใน Rust คือ ตัวแปรเป็นแบบ ไม่เปลี่ยนรูป (immutable) โดยค่าเริ่มต้น ซึ่งหมายความว่าเมื่อค่าถูกผูกกับชื่อตัวแปรแล้ว คุณจะไม่สามารถเปลี่ยนค่านั้นได้ในภายหลัง

let x = 10;
// x = 15; // บรรทัดนี้จะทำให้เกิดข้อผิดพลาดเวลาคอมไพล์! ไม่สามารถกำหนดค่าให้ตัวแปร `x` ที่ไม่เปลี่ยนรูปได้สองครั้ง
println!("ค่าของ x คือ: {}", x); // {} เป็นตัวยึดตำแหน่งสำหรับค่าของ x

การไม่เปลี่ยนรูปโดยค่าเริ่มต้นนี้เป็นการตัดสินใจออกแบบโดยเจตนา ซึ่งช่วยให้คุณเขียนโค้ดที่ปลอดภัยและคาดการณ์ได้มากขึ้น โดยป้องกันการแก้ไขข้อมูลโดยไม่ได้ตั้งใจ ซึ่งอาจเป็นสาเหตุทั่วไปของข้อบกพร่อง หากคุณ ต้องการ ตัวแปรที่ค่าสามารถเปลี่ยนแปลงได้ คุณต้องระบุอย่างชัดเจนว่าเป็น mutable โดยใช้คำหลัก mut ระหว่างการประกาศ

let mut count = 0; // ประกาศ 'count' เป็น mutable
println!("จำนวนเริ่มต้น: {}", count);
count = 1; // สิ่งนี้ทำได้เพราะ 'count' ถูกประกาศด้วย 'mut'
println!("จำนวนใหม่: {}", count);

Rust ยังอนุญาต การบังเงา (shadowing) คุณสามารถประกาศตัวแปร ใหม่ ที่มีชื่อเดียวกับตัวแปรก่อนหน้าภายในขอบเขตเดียวกัน ตัวแปรใหม่จะ “บังเงา” ตัวแปรเก่า ซึ่งหมายความว่าการใช้ชื่อนั้นในภายหลังจะอ้างถึงตัวแปรใหม่ สิ่งนี้แตกต่างจากการเปลี่ยนแปลงค่า (mutation) เพราะเรากำลังสร้างตัวแปรใหม่ทั้งหมด ซึ่งอาจมีประเภทข้อมูลแตกต่างกันได้ด้วยซ้ำ

let spaces = "   "; // 'spaces' เริ่มต้นเป็นสตริงสไลซ์ (&str)
let spaces = spaces.len(); // 'spaces' ถูกบังเงาโดยตัวแปรใหม่ที่เก็บความยาว (เป็นจำนวนเต็ม, usize)
println!("จำนวนช่องว่าง: {}", spaces); // พิมพ์ค่าจำนวนเต็ม

ประเภทข้อมูลพื้นฐาน (Basic Data Types)

Rust เป็นภาษา ประเภทข้อมูลคงที่ (statically typed) ซึ่งหมายความว่าประเภทของตัวแปรทุกตัวจะต้องเป็นที่รู้จักโดยคอมไพเลอร์ ณ เวลาคอมไพล์ อย่างไรก็ตาม Rust มี การอนุมานประเภทข้อมูล (type inference) ที่ยอดเยี่ยม ในหลายสถานการณ์ คุณไม่จำเป็นต้องเขียนประเภทข้อมูลออกมาอย่างชัดเจน คอมไพเลอร์มักจะสามารถอนุมานได้จากค่าและวิธีที่คุณใช้งาน

let quantity = 10;         // คอมไพเลอร์อนุมานเป็น i32 (ประเภทจำนวนเต็มมีเครื่องหมายเริ่มต้น)
let price = 9.99;          // คอมไพเลอร์อนุมานเป็น f64 (ประเภททศนิยมเริ่มต้น)
let active = true;         // คอมไพเลอร์อนุมานเป็น bool (บูลีน)
let initial = 'R';         // คอมไพเลอร์อนุมานเป็น char (อักขระ)

หากคุณต้องการหรือจำเป็นต้องระบุอย่างชัดเจน (เช่น เพื่อความชัดเจน หรือเมื่อคอมไพเลอร์ต้องการความช่วยเหลือ) คุณสามารถให้ คำอธิบายประเภทข้อมูล (type annotations) โดยใช้เครื่องหมายโคลอน : ตามด้วยชื่อประเภท

let score: i32 = 100;       // ระบุชัดเจนว่าเป็นจำนวนเต็ม 32 บิตมีเครื่องหมาย
let ratio: f32 = 0.5;       // ระบุชัดเจนว่าเป็นทศนิยมความแม่นยำเดี่ยว
let is_complete: bool = false; // ระบุชัดเจนว่าเป็นบูลีน
let grade: char = 'A';      // ระบุชัดเจนว่าเป็นอักขระ (ค่าสเกลาร์ Unicode)

Rust มีประเภทข้อมูล สเกลาร์ (scalar) (แทนค่าเดี่ยว) ในตัวหลายประเภท:

  • จำนวนเต็ม (Integers): จำนวนเต็มมีเครื่องหมาย (i8, i16, i32, i64, i128, isize) เก็บจำนวนเต็มทั้งบวกและลบ จำนวนเต็มไม่มีเครื่องหมาย (u8, u16, u32, u64, u128, usize) เก็บเฉพาะจำนวนเต็มที่ไม่เป็นลบ ประเภท isize และ usize ขึ้นอยู่กับสถาปัตยกรรมของคอมพิวเตอร์ (32 บิตหรือ 64 บิต) และใช้เป็นหลักสำหรับการทำดัชนีคอลเลกชัน
  • จำนวนทศนิยม (Floating-Point Numbers): f32 (ความแม่นยำเดี่ยว) และ f64 (ความแม่นยำคู่) ค่าเริ่มต้นคือ f64
  • บูลีน (Booleans): ประเภท bool มีสองค่าที่เป็นไปได้: true หรือ false
  • อักขระ (Characters): ประเภท char แทนค่าสเกลาร์ Unicode เดียว (ครอบคลุมมากกว่าแค่อักขระ ASCII) อยู่ในเครื่องหมายคำพูดเดี่ยว (เช่น 'z', 'π', '🚀')

สตริง: String กับ &str

การจัดการข้อความใน Rust มักเกี่ยวข้องกับประเภทข้อมูลหลักสองประเภท ซึ่งอาจทำให้ผู้มาใหม่สับสน:

  1. &str (อ่านว่า “สตริงสไลซ์”): นี่คือ การอ้างอิงแบบไม่เปลี่ยนรูป (immutable reference) ไปยังลำดับของไบต์ที่เข้ารหัสแบบ UTF-8 ซึ่งเก็บไว้ที่ใดที่หนึ่งในหน่วยความจำ สตริงลิเทอรัล (เช่น "Hello") เป็นประเภท &'static str ซึ่งหมายความว่ามันถูกเก็บไว้โดยตรงในไบนารีของโปรแกรมและมีอยู่ตลอดระยะเวลาการทำงานของโปรแกรม สไลซ์ให้ มุมมอง (view) ข้อมูลสตริง โดยไม่ได้เป็นเจ้าของ (owning) มันมีขนาดคงที่
  2. String: นี่คือประเภทสตริงที่เข้ารหัสแบบ UTF-8 ซึ่ง มีเจ้าของ (owned), ขยายได้ (growable), เปลี่ยนแปลงได้ (mutable) ข้อมูล String ถูกเก็บไว้บน ฮีป (heap) ทำให้สามารถปรับขนาดได้ โดยทั่วไปคุณจะใช้ String เมื่อคุณต้องการแก้ไขข้อมูลสตริง หรือเมื่อสตริงต้องการเป็นเจ้าของข้อมูลของตัวเองและจัดการช่วงชีวิตของตัวเอง (มักจะเมื่อคืนค่าสตริงจากฟังก์ชันหรือเก็บไว้ในสตรัค)
// สตริงลิเทอรัล (เก็บไว้ในไบนารีของโปรแกรม, ไม่เปลี่ยนรูป)
let static_slice: &'static str = "ฉันไม่เปลี่ยนรูป";

// สร้าง String ที่มีเจ้าของและจัดสรรบนฮีปจากลิเทอรัล
let mut dynamic_string: String = String::from("เริ่มต้น");

// แก้ไข String (ทำได้เพราะเป็น mutable และเป็นเจ้าของข้อมูล)
dynamic_string.push_str(" และเติบโต");
println!("{}", dynamic_string); // ผลลัพธ์: เริ่มต้น และเติบโต

// สร้างสตริงสไลซ์ที่อ้างอิงส่วนหนึ่งของ String
// สไลซ์นี้ยืมข้อมูลจาก dynamic_string
let slice_from_string: &str = &dynamic_string[0..5]; // อ้างอิง "เริ่มต้น"
println!("สไลซ์: {}", slice_from_string);

ฟังก์ชัน (Functions)

ฟังก์ชันเป็นพื้นฐานสำหรับการจัดระเบียบโค้ดเป็นหน่วยที่มีชื่อและนำกลับมาใช้ใหม่ได้ เราได้เจอฟังก์ชันพิเศษ main ไปแล้ว

// การกำหนดฟังก์ชัน
fn greet(name: &str) { // รับพารามิเตอร์หนึ่งตัว: 'name' ซึ่งเป็นสตริงสไลซ์ (&str)
    println!("สวัสดี, {}!", name);
}

// ฟังก์ชันที่รับพารามิเตอร์ i32 สองตัวและคืนค่า i32
fn add(a: i32, b: i32) -> i32 {
    // ใน Rust นิพจน์สุดท้ายในเนื้อหาฟังก์ชันจะถูกส่งคืนโดยอัตโนมัติ
    // ตราบใดที่มันไม่ได้ลงท้ายด้วยเครื่องหมายอัฒภาค
    a + b
    // สิ่งนี้เทียบเท่ากับการเขียน: return a + b;
}

fn main() {
    greet("อลิซ"); // เรียกใช้ฟังก์ชัน 'greet'

    let sum = add(5, 3); // เรียกใช้ 'add', ผูกค่าที่ส่งคืนกับ 'sum'
    println!("5 + 3 = {}", sum); // ผลลัพธ์: 5 + 3 = 8
}

ประเด็นสำคัญเกี่ยวกับฟังก์ชัน:

  • ใช้คำหลัก fn เพื่อประกาศฟังก์ชัน
  • ระบุชื่อพารามิเตอร์และประเภทข้อมูลภายในวงเล็บ
  • ระบุประเภทข้อมูลที่ส่งคืนของฟังก์ชันหลังลูกศร -> หากฟังก์ชันไม่คืนค่า ประเภทข้อมูลที่ส่งคืนจะเป็น () โดยปริยาย (ทูเพิลว่างเปล่า มักเรียกว่า “unit type”)
  • เนื้อหาฟังก์ชันประกอบด้วยชุดของ คำสั่ง (statements) (คำสั่งที่ดำเนินการ มักลงท้ายด้วย ;) และอาจลงท้ายด้วย นิพจน์ (expression) (สิ่งที่ประเมินค่าได้)
  • ค่าของนิพจน์สุดท้ายในบล็อกฟังก์ชันจะถูกส่งคืนโดยอัตโนมัติหากไม่มีเครื่องหมายอัฒภาค คำหลัก return สามารถใช้สำหรับการส่งคืนค่าก่อนกำหนดอย่างชัดเจนจากที่ใดก็ได้ภายในฟังก์ชัน

การควบคุมการไหล (Control Flow)

Rust มีโครงสร้างการควบคุมการไหลมาตรฐานเพื่อกำหนดลำดับการทำงานของโค้ด:

  • if/else/else if: ใช้สำหรับการทำงานตามเงื่อนไข
let number = 6;

if number % 4 == 0 {
    println!("number หารด้วย 4 ลงตัว");
} else if number % 3 == 0 {
    println!("number หารด้วย 3 ลงตัว"); // สาขานี้ทำงาน
} else {
    println!("number หารด้วย 4 หรือ 3 ไม่ลงตัว");
}

// ที่สำคัญ 'if' เป็นนิพจน์ใน Rust หมายความว่ามันประเมินค่าได้
// สิ่งนี้ทำให้คุณสามารถใช้มันโดยตรงในคำสั่ง 'let' ได้
let condition = true;
let value = if condition { 5 } else { 6 }; // value จะเป็น 5
println!("ค่าคือ: {}", value);
// หมายเหตุ: ทั้งสองสาขาของนิพจน์ 'if' ต้องประเมินค่าเป็นประเภทเดียวกัน
  • ลูป (Loops): ใช้สำหรับการทำงานซ้ำ
    • loop: สร้างลูปไม่สิ้นสุด โดยทั่วไปคุณจะใช้ break เพื่อออกจากลูป ซึ่งสามารถส่งคืนค่าได้
    • while: ทำงานตราบเท่าที่เงื่อนไขที่ระบุยังคงเป็นจริง
    • for: วนซ้ำตามองค์ประกอบของคอลเลกชันหรือช่วง นี่เป็นประเภทลูปที่ใช้บ่อยที่สุดและมักจะปลอดภัยที่สุดใน Rust
// ตัวอย่าง loop
let mut counter = 0;
let result = loop {
    counter += 1;
    if counter == 10 {
        break counter * 2; // ออกจากลูปและคืนค่า 'counter * 2'
    }
};
println!("ผลลัพธ์ลูป: {}", result); // ผลลัพธ์: ผลลัพธ์ลูป: 20

// ตัวอย่าง while
let mut num = 3;
while num != 0 {
    println!("{}!", num);
    num -= 1;
}
println!("ปล่อยตัว!!!");

// ตัวอย่าง for (วนซ้ำในอาร์เรย์)
let a = [10, 20, 30, 40, 50];
for element in a.iter() { // .iter() สร้าง iterator เหนือองค์ประกอบของอาร์เรย์
    println!("ค่าคือ: {}", element);
}

// ตัวอย่าง for (วนซ้ำในช่วง)
// (1..4) สร้างช่วงที่รวม 1, 2, 3 (ไม่รวม 4)
// .rev() กลับลำดับ iterator
for number in (1..4).rev() {
    println!("{}!", number); // พิมพ์ 3!, 2!, 1!
}
println!("ปล่อยตัวอีกครั้ง!!!");

คอมเมนต์ (Comments)

ใช้คอมเมนต์เพื่อเพิ่มคำอธิบายและบันทึกย่อในโค้ดของคุณ ซึ่งคอมไพเลอร์จะละเว้น

// นี่คือคอมเมนต์บรรทัดเดียว มันขยายไปจนถึงสิ้นสุดบรรทัด

/*
 * นี่คือคอมเมนต์แบบบล็อกหลายบรรทัด
 * มันสามารถครอบคลุมหลายบรรทัดและมีประโยชน์
 * สำหรับคำอธิบายที่ยาวขึ้น
 */

 let lucky_number = 7; // คุณสามารถวางคอมเมนต์ไว้ที่ท้ายบรรทัดได้เช่นกัน

ทำความเข้าใจความเป็นเจ้าของ (Ownership): แนวคิดหลักของ Rust

ความเป็นเจ้าของ (Ownership) เป็นคุณสมบัติที่โดดเด่นและเป็นศูนย์กลางที่สุดของ Rust มันเป็นกลไกที่ช่วยให้ Rust รับประกันความปลอดภัยของหน่วยความจำ ณ เวลาคอมไพล์ โดยไม่จำเป็นต้องมี garbage collector การเข้าใจความเป็นเจ้าของเป็นกุญแจสำคัญในการทำความเข้าใจ Rust มันปฏิบัติตามกฎหลักสามข้อ:

  1. เจ้าของ (Owner): แต่ละค่าใน Rust มีตัวแปรที่ถูกกำหนดให้เป็น เจ้าของ
  2. เจ้าของเพียงคนเดียว (One Owner): สามารถมีเจ้าของของค่าเฉพาะได้ เพียงคนเดียว ณ เวลาใดเวลาหนึ่ง
  3. ขอบเขตและการทิ้ง (Scope & Drop): เมื่อตัวแปรที่เป็นเจ้าของออกนอกขอบเขต (เช่น ฟังก์ชันที่ประกาศสิ้นสุดลง) ค่าที่มันเป็นเจ้าของจะถูก ทิ้ง (dropped) ซึ่งหมายความว่าหน่วยความจำของมันจะถูกปล่อยคืนโดยอัตโนมัติ
{ // s ไม่สามารถใช้งานได้ที่นี่ ยังไม่ได้ประกาศ
    let s = String::from("hello"); // s ใช้งานได้ตั้งแต่จุดนี้เป็นต้นไป;
                                    // s 'เป็นเจ้าของ' ข้อมูล String ที่จัดสรรบนฮีป
    // คุณสามารถใช้ s ที่นี่ได้
    println!("{}", s);
} // ขอบเขตสิ้นสุดที่นี่ 's' ไม่สามารถใช้งานได้อีกต่อไป
  // Rust เรียกใช้ฟังก์ชันพิเศษ 'drop' โดยอัตโนมัติสำหรับ String ที่ 's' เป็นเจ้าของ
  // เพื่อปล่อยคืนหน่วยความจำฮีปของมัน

เมื่อคุณกำหนดค่าที่มีเจ้าของ (เช่น String, Vec, หรือสตรัคที่มีประเภทข้อมูลที่มีเจ้าของ) ให้กับตัวแปรอื่น หรือส่งผ่านไปยังฟังก์ชันตามค่า ความเป็นเจ้าของจะถูก ย้าย (moved) ตัวแปรเดิมจะไม่สามารถใช้งานได้อีกต่อไป

let s1 = String::from("original");
let s2 = s1; // ความเป็นเจ้าของข้อมูล String ถูก ย้าย จาก s1 ไปยัง s2
             // s1 ไม่ถือว่าใช้งานได้อีกต่อไปหลังจากจุดนี้

// println!("s1 คือ: {}", s1); // ข้อผิดพลาดเวลาคอมไพล์! ค่าถูกยืมที่นี่หลังจากการย้าย
                              // s1 ไม่ได้เป็นเจ้าของข้อมูลอีกต่อไป
println!("s2 คือ: {}", s2); // s2 ตอนนี้เป็นเจ้าของและใช้งานได้

พฤติกรรมการย้ายนี้ป้องกันข้อผิดพลาด “double free” ซึ่งตัวแปรสองตัวอาจพยายามปล่อยคืนตำแหน่งหน่วยความจำเดียวกันโดยไม่ได้ตั้งใจเมื่อออกนอกขอบเขต ประเภทข้อมูลพื้นฐาน เช่น จำนวนเต็ม ทศนิยม บูลีน และอักขระ ใช้เทรต Copy ซึ่งหมายความว่ามันจะถูกคัดลอกแทนที่จะถูกย้ายเมื่อกำหนดค่าหรือส่งผ่านไปยังฟังก์ชัน

การยืมและการอ้างอิง (Borrowing and References)

จะทำอย่างไรถ้าคุณต้องการให้ฟังก์ชันใช้ค่า โดยไม่ต้อง โอนความเป็นเจ้าของ? คุณสามารถสร้าง การอ้างอิง (references) การสร้างการอ้างอิงเรียกว่า การยืม (borrowing) การอ้างอิงช่วยให้คุณเข้าถึงข้อมูลที่ตัวแปรอื่นเป็นเจ้าของได้โดยไม่ต้องรับความเป็นเจ้าของ

// ฟังก์ชันนี้รับการอ้างอิง (&) ไปยัง String
// มันยืม String แต่ไม่ได้รับความเป็นเจ้าของ
fn calculate_length(s: &String) -> usize {
    s.len()
} // ที่นี่ s (การอ้างอิง) ออกนอกขอบเขต แต่เนื่องจากมันไม่ได้เป็นเจ้าของ
  // ข้อมูล String ข้อมูลจึง ไม่ได้ ถูกทิ้งเมื่อการอ้างอิงออกนอกขอบเขต

fn main() {
    let s1 = String::from("hello");

    // เราส่งการอ้างอิงไปยัง s1 โดยใช้สัญลักษณ์ '&'
    // s1 ยังคงเป็นเจ้าของข้อมูล String
    let len = calculate_length(&s1);

    // s1 ยังคงใช้งานได้ที่นี่เพราะความเป็นเจ้าของไม่เคยถูกย้าย
    println!("ความยาวของ '{}' คือ {}.", s1, len);
}

การอ้างอิงเป็นแบบไม่เปลี่ยนรูปโดยค่าเริ่มต้น เช่นเดียวกับตัวแปร หากคุณต้องการแก้ไขข้อมูลที่ยืมมา คุณต้องมี การอ้างอิงแบบเปลี่ยนแปลงได้ (mutable reference) ซึ่งแสดงด้วย &mut อย่างไรก็ตาม Rust บังคับใช้กฎที่เข้มงวดเกี่ยวกับการอ้างอิงแบบเปลี่ยนแปลงได้เพื่อป้องกัน data races:

กฎการยืม (The Borrowing Rules):

  1. ณ เวลาใดเวลาหนึ่ง คุณสามารถมี อย่างใดอย่างหนึ่ง :
    • หนึ่ง การอ้างอิงแบบเปลี่ยนแปลงได้ (&mut T)
    • จำนวนเท่าใดก็ได้ ของการอ้างอิงแบบไม่เปลี่ยนรูป (&T)
  2. การอ้างอิงต้องถูกต้องเสมอ (ไม่สามารถมีช่วงชีวิตยาวนานกว่าข้อมูลที่ชี้ไป – สิ่งนี้จัดการโดย lifetimes ซึ่งมักจะเป็นไปโดยปริยาย)

กฎเหล่านี้บังคับใช้โดยคอมไพเลอร์

// ฟังก์ชันนี้รับการอ้างอิงแบบเปลี่ยนแปลงได้ไปยัง String
fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

fn main() {
    // 's' ต้องประกาศเป็น mutable เพื่ออนุญาตการยืมแบบเปลี่ยนแปลงได้
    let mut s = String::from("hello");

    // ตัวอย่างการบังคับใช้กฎการยืม (ยกเลิกคอมเมนต์บรรทัดเพื่อดูข้อผิดพลาด):
    // let r1 = &s; // การยืมแบบไม่เปลี่ยนรูป - OK
    // let r2 = &s; // การยืมแบบไม่เปลี่ยนรูปอีกอัน - OK (อนุญาตการยืมแบบไม่เปลี่ยนรูปหลายรายการ)
    // let r3 = &mut s; // ERROR! ไม่สามารถยืม `s` เป็น mutable ในขณะที่การยืมแบบไม่เปลี่ยนรูปยังใช้งานอยู่
    //                  // Rust บังคับใช้: ผู้อ่านหลายคน (&T) หรือผู้เขียนคนเดียว (&mut T) อย่างใดอย่างหนึ่ง ไม่ใช่ทั้งคู่
    // println!("{}, {}", r1, r2); // การใช้ r1/r2 ทำให้มันยังคงใช้งานอยู่ ซึ่งกระตุ้นให้เกิดข้อผิดพลาด
    //                             // หากไม่มี println นี้ NLL (Non-Lexical Lifetimes) ของ Rust จะปล่อย r1/r2 ก่อนเวลา
    //                             // ทำให้ &mut s ใช้งานได้ที่นี่

    // การยืมแบบเปลี่ยนแปลงได้ได้รับอนุญาตที่นี่เพราะไม่มีการยืมอื่น ๆ ที่ใช้งานอยู่
    change(&mut s);
    println!("{}", s); // ผลลัพธ์: hello, world
}

ประเภทข้อมูลประกอบ (Compound Data Types)

Rust มีวิธีจัดกลุ่มค่าหลายค่าเป็นประเภทข้อมูลที่ซับซ้อนมากขึ้น

สตรัค (Structs)

สตรัค (ย่อมาจาก structures) ช่วยให้คุณกำหนดประเภทข้อมูลแบบกำหนดเองโดยการจัดกลุ่มฟิลด์ข้อมูลที่เกี่ยวข้องเข้าด้วยกันภายใต้ชื่อเดียว

// กำหนดสตรัคชื่อ User
struct User {
    active: bool,
    username: String, // ใช้ประเภท String ที่มีเจ้าของ
    email: String,
    sign_in_count: u64,
}

fn main() {
    // สร้าง instance ของสตรัค User
    // instance ต้องให้ค่าสำหรับทุกฟิลด์
    let mut user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };

    // เข้าถึงฟิลด์สตรัคโดยใช้เครื่องหมายจุด
    // instance ต้องเป็น mutable เพื่อเปลี่ยนค่าฟิลด์
    user1.email = String::from("anotheremail@example.com");

    println!("อีเมลผู้ใช้: {}", user1.email);

    // ใช้ฟังก์ชันช่วยเหลือเพื่อสร้าง instance ของ User
    let user2 = build_user(String::from("user2@test.com"), String::from("user2"));
    println!("สถานะ active ของ User 2: {}", user2.active);

    // ไวยากรณ์การอัปเดตสตรัค: สร้าง instance ใหม่โดยใช้บางฟิลด์
    // จาก instance ที่มีอยู่สำหรับฟิลด์ที่เหลือ
    let user3 = User {
        email: String::from("user3@domain.com"),
        username: String::from("user3"),
        ..user2 // รับค่า 'active' และ 'sign_in_count' จาก user2
    };
    println!("จำนวนการลงชื่อเข้าใช้ของ User 3: {}", user3.sign_in_count);
}

// ฟังก์ชันที่คืนค่า instance ของ User
fn build_user(email: String, username: String) -> User {
    User {
        email, // การเริ่มต้นฟิลด์แบบย่อ: ถ้าชื่อพารามิเตอร์ตรงกับชื่อฟิลด์
        username,
        active: true,
        sign_in_count: 1,
    }
}

Rust ยังรองรับ ทูเพิลสตรัค (tuple structs) ซึ่งเป็นทูเพิลที่มีชื่อ (เช่น struct Color(i32, i32, i32);) และ สตรัคแบบหน่วย (unit-like structs) ซึ่งไม่มีฟิลด์และมีประโยชน์เมื่อคุณต้องการ implement trait บนประเภทข้อมูลแต่ไม่จำเป็นต้องเก็บข้อมูลใดๆ (เช่น struct AlwaysEqual;)

อีนัม (Enums)

อีนัม (enumerations) ช่วยให้คุณกำหนดประเภทข้อมูลโดยการแจกแจง variant ที่เป็นไปได้ ค่าของอีนัมสามารถเป็นได้เพียงหนึ่งใน variant ที่เป็นไปได้เท่านั้น

// อีนัมง่ายๆ ที่กำหนดชนิดของที่อยู่ IP
enum IpAddrKind {
    V4, // Variant ที่ 1
    V6, // Variant ที่ 2
}

// Variant ของอีนัมสามารถเก็บข้อมูลที่เกี่ยวข้องได้ด้วย
enum IpAddr {
    V4(u8, u8, u8, u8), // Variant V4 เก็บค่า u8 สี่ค่า
    V6(String),         // Variant V6 เก็บ String
}

// อีนัมที่พบบ่อยและสำคัญมากในไลบรารีมาตรฐานของ Rust: Option<T>
// มันเข้ารหัสแนวคิดของค่าที่อาจมีอยู่หรือไม่มีอยู่
// enum Option<T> {
//     Some(T), // แทนการมีอยู่ของค่าประเภท T
//     None,    // แทนการไม่มีอยู่ของค่า
// }

// อีนัมสำคัญอีกตัวในไลบรารีมาตรฐาน: Result<T, E>
// ใช้สำหรับการดำเนินการที่สามารถสำเร็จ (Ok) หรือล้มเหลว (Err) ได้
// enum Result<T, E> {
//     Ok(T),   // แทนความสำเร็จ ประกอบด้วยค่าประเภท T
//     Err(E),  // แทนความล้มเหลว ประกอบด้วยค่าข้อผิดพลาดประเภท E
// }

fn main() {
    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6;

    // การสร้าง instance ของอีนัม IpAddr พร้อมข้อมูลที่เกี่ยวข้อง
    let home = IpAddr::V4(127, 0, 0, 1);
    let loopback = IpAddr::V6(String::from("::1"));

    // ตัวอย่างกับ Option<T>
    let some_number: Option<i32> = Some(5);
    let no_number: Option<i32> = None;

    // ตัวดำเนินการควบคุมการไหล 'match' เหมาะอย่างยิ่งสำหรับการทำงานกับอีนัม
    // มันช่วยให้คุณสามารถเรียกใช้โค้ดที่แตกต่างกันตาม variant ของอีนัม
    match some_number {
        Some(i) => println!("ได้ตัวเลข: {}", i), // ถ้าเป็น Some ให้ผูกค่าภายในกับ i
        None => println!("ไม่ได้อะไรเลย."),           // ถ้าเป็น None
    }

    // 'match' ต้องครอบคลุมทุกกรณี (exhaustive): คุณต้องจัดการทุก variant ที่เป็นไปได้
    // เครื่องหมายขีดล่าง '_' สามารถใช้เป็นรูปแบบ wildcard เพื่อจับ variant ใดๆ
    // ที่ไม่ได้ระบุไว้อย่างชัดเจน
}

Option<T> และ Result<T, E> เป็นศูนย์กลางของแนวทางของ Rust ในการจัดการค่าที่อาจหายไปและข้อผิดพลาดอย่างมีเสถียรภาพ

ข้อมูลเบื้องต้นเกี่ยวกับ Cargo: เครื่องมือสร้างและตัวจัดการแพ็กเกจของ Rust

Cargo เป็นส่วนที่ขาดไม่ได้ของระบบนิเวศ Rust ซึ่งช่วยปรับปรุงกระบวนการสร้าง ทดสอบ และจัดการโปรเจกต์ Rust ให้คล่องตัวขึ้น เป็นหนึ่งในคุณสมบัติที่นักพัฒนามักชื่นชม

  • Cargo.toml: นี่คือไฟล์ manifest สำหรับโปรเจกต์ Rust ของคุณ เขียนในรูปแบบ TOML (Tom's Obvious, Minimal Language) ประกอบด้วยข้อมูลเมตาที่สำคัญเกี่ยวกับโปรเจกต์ของคุณ (เช่น ชื่อ เวอร์ชัน และผู้เขียน) และที่สำคัญคือ รายการ การพึ่งพา (dependencies) (เครตภายนอกอื่น ๆ ที่โปรเจกต์ของคุณต้องใช้)
    [package]
    name = "my_project"
    version = "0.1.0"
    edition = "2021" # ระบุ Rust edition ที่จะใช้ (มีผลต่อคุณสมบัติภาษา)
    
    # การพึ่งพาจะอยู่ด้านล่าง
    [dependencies]
    # ตัวอย่าง: เพิ่มเครต 'rand' สำหรับการสร้างเลขสุ่ม
    # rand = "0.8.5"
    # เมื่อคุณ build, Cargo จะดาวน์โหลดและคอมไพล์ 'rand' และการพึ่งพาของมัน
    
  • cargo new <project_name>: สร้างโครงสร้างโปรเจกต์แอปพลิเคชันไบนารี (เรียกใช้งานได้) ใหม่
  • cargo new --lib <library_name>: สร้างโครงสร้างโปรเจกต์ไลบรารีใหม่ (เครตที่ออกแบบมาเพื่อใช้โดยโปรแกรมอื่น)
  • cargo build: คอมไพล์โปรเจกต์ของคุณและการพึ่งพา โดยค่าเริ่มต้น จะสร้างบิลด์ debug ที่ไม่ได้ปรับให้เหมาะสม ผลลัพธ์จะอยู่ในไดเรกทอรี target/debug/
  • cargo build --release: คอมไพล์โปรเจกต์ของคุณโดยเปิดใช้งานการปรับให้เหมาะสม เหมาะสำหรับการแจกจ่ายหรือการทดสอบประสิทธิภาพ ผลลัพธ์จะอยู่ในไดเรกทอรี target/release/
  • cargo run: คอมไพล์ (หากจำเป็น) และรันโปรเจกต์ไบนารีของคุณ
  • cargo check: ตรวจสอบโค้ดของคุณเพื่อหาข้อผิดพลาดในการคอมไพล์อย่างรวดเร็วโดยไม่ต้องสร้างไฟล์ปฏิบัติการจริง โดยทั่วไปจะเร็วกว่า cargo build มากและมีประโยชน์ระหว่างการพัฒนาเพื่อรับข้อเสนอแนะอย่างรวดเร็ว
  • cargo test: รันการทดสอบใดๆ ที่กำหนดไว้ภายในโปรเจกต์ของคุณ (โดยปกติจะอยู่ในไดเรกทอรี src หรือในไดเรกทอรี tests แยกต่างหาก)

เมื่อคุณเพิ่มการพึ่งพาลงในไฟล์ Cargo.toml ของคุณ แล้วรันคำสั่ง เช่น cargo build หรือ cargo run, Cargo จะจัดการดาวน์โหลดเครตที่ต้องการ (และ การพึ่งพา ของมัน) จากคลังเก็บส่วนกลาง crates.io และคอมไพล์ทุกอย่างเข้าด้วยกันโดยอัตโนมัติ

ขั้นตอนต่อไปในการเดินทาง Rust ของคุณ

คู่มือเริ่มต้นนี้ได้ครอบคลุมพื้นฐานที่จำเป็นเพื่อให้คุณเริ่มต้นได้ ภาษา Rust ยังมีคุณสมบัติที่ทรงพลังอีกมากมายให้สำรวจเมื่อคุณก้าวหน้า:

  • การจัดการข้อผิดพลาด (Error Handling): การเรียนรู้ Result enum อย่างเชี่ยวชาญ, การส่งต่อข้อผิดพลาดโดยใช้ตัวดำเนินการ ?, และการกำหนดประเภทข้อผิดพลาดแบบกำหนดเอง
  • คอลเลกชัน (Collections): การทำงานอย่างมีประสิทธิภาพกับโครงสร้างข้อมูลทั่วไป เช่น Vec<T> (อาร์เรย์/เวกเตอร์แบบไดนามิก), HashMap<K, V> (แฮชแมป), HashSet<T>, ฯลฯ
  • เจเนอริก (Generics): การเขียนโค้ดที่ยืดหยุ่นและนำกลับมาใช้ใหม่ได้ซึ่งสามารถทำงานกับประเภทข้อมูลต่างๆ โดยใช้พารามิเตอร์ประเภท (<T>)
  • เทรต (Traits): การกำหนดพฤติกรรมร่วมและอินเทอร์เฟซที่ประเภทข้อมูลสามารถ implement ได้ (คล้ายกับอินเทอร์เฟซในภาษาอื่น แต่ทรงพลังกว่า)
  • ช่วงชีวิต (Lifetimes): การทำความเข้าใจว่า Rust รับประกันว่าการอ้างอิงถูกต้องเสมอได้อย่างไร ซึ่งบางครั้งต้องใช้คำอธิบายช่วงชีวิต ('a) อย่างชัดเจน
  • การทำงานพร้อมกัน (Concurrency): การสำรวจเธรด, แชนเนลสำหรับการส่งข้อความ, สถานะที่ใช้ร่วมกันด้วย Mutex และ Arc, และไวยากรณ์ async/await ที่ทรงพลังของ Rust สำหรับการเขียนโปรแกรมแบบอะซิงโครนัส
  • มาโคร (Macros): การเรียนรู้ที่จะเขียนมาโครของคุณเองสำหรับการสร้างโค้ด ณ เวลาคอมไพล์ขั้นสูง
  • โมดูล (Modules): การจัดระเบียบโปรเจกต์ขนาดใหญ่เป็นหน่วยตรรกะ, การควบคุมการมองเห็น (public/private) ของรายการต่างๆ
  • การทดสอบ (Testing): การเขียนการทดสอบหน่วย (unit), การทดสอบการรวม (integration), และการทดสอบเอกสาร (documentation tests) อย่างมีประสิทธิภาพโดยใช้เฟรมเวิร์กการทดสอบในตัวของ Rust

สรุป

Rust นำเสนอข้อเสนอที่ไม่เหมือนใครและน่าสนใจ: ประสิทธิภาพดิบที่คาดหวังจากภาษาระดับต่ำเช่น C++ ควบคู่ไปกับการรับประกันความปลอดภัย ณ เวลาคอมไพล์ที่แข็งแกร่ง ซึ่งช่วยขจัดข้อบกพร่องทั่วไปทั้งประเภท โดยเฉพาะอย่างยิ่งที่เกี่ยวกับ การจัดการหน่วยความจำและการทำงานพร้อมกัน แม้ว่าช่วงการเรียนรู้จะเกี่ยวข้องกับการทำความเข้าใจแนวคิดใหม่ๆ เช่น ความเป็นเจ้าของและการยืม (และการรับฟังข้อความแสดงข้อผิดพลาดของคอมไพเลอร์ที่บางครั้งเข้มงวด) แต่ผลตอบแทนที่ได้คือซอฟต์แวร์ที่มีความน่าเชื่อถือสูง มีประสิทธิภาพ และมักจะง่ายต่อการบำรุงรักษาในระยะยาว ด้วยเครื่องมือที่ยอดเยี่ยมผ่าน Cargo และชุมชนที่ให้การสนับสนุน Rust จึงเป็นภาษาที่คุ้มค่าแก่การเรียนรู้

เริ่มต้นเล็กๆ ทดลองใช้ Cargo ยอมรับข้อความแสดงข้อผิดพลาดที่เป็นประโยชน์ (แม้บางครั้งจะยาว) ของคอมไพเลอร์เป็นแนวทาง แล้วคุณจะอยู่บนเส้นทางสู่การเรียนรู้ภาษาที่ทรงพลังและได้รับความนิยมเพิ่มขึ้นเรื่อยๆ นี้ ขอให้สนุกกับการเขียน Rust!

บทความที่เกี่ยวข้อง