Hello, Cargo!
Cargo คือ build system และ package manager ของ Rust Rustacean ส่วนใหญ่ใช้ เครื่องมือนี้จัดการโปรเจกต์ Rust ของตน เพราะ Cargo จัดการงานหลายอย่างให้ คุณ เช่น build โค้ด, download library ที่โค้ดของคุณพึ่งพา และ build library เหล่านั้น (เราเรียก library ที่โค้ดของคุณต้องการว่า dependency)
โปรแกรม Rust ที่ง่ายที่สุดอย่างที่เราเขียนมา ไม่มี dependency เลย ถ้าเรา build โปรเจกต์ “Hello, world!” ด้วย Cargo มันก็จะใช้แค่ส่วนของ Cargo ที่ จัดการการ build โค้ด เมื่อคุณเขียนโปรแกรม Rust ที่ซับซ้อนขึ้น คุณจะเพิ่ม dependency และถ้าคุณเริ่มโปรเจกต์ด้วย Cargo การเพิ่ม dependency จะทำได้ ง่ายกว่ามาก
เพราะโปรเจกต์ Rust ส่วนใหญ่ใช้ Cargo เนื้อหาที่เหลือของหนังสือเล่มนี้จึง สมมติว่าคุณใช้ Cargo เช่นกัน Cargo มาพร้อม Rust ถ้าคุณใช้ installer ทางการ ที่พูดถึงในส่วน “การติดตั้ง” ถ้าคุณติดตั้ง Rust ด้วยวิธีอื่น ให้เช็คว่า Cargo ติดตั้งอยู่หรือไม่ โดยป้อนคำสั่งต่อไปนี้ ใน terminal:
$ cargo --version
ถ้าคุณเห็นเลข version แสดงว่ามี! ถ้าเห็น error อย่าง command not found
ให้ดู documentation ของวิธีติดตั้งที่คุณใช้ เพื่อหาวิธีติดตั้ง Cargo แยก
ต่างหาก
สร้างโปรเจกต์ด้วย Cargo
มาสร้างโปรเจกต์ใหม่ด้วย Cargo แล้วดูว่ามันต่างจากโปรเจกต์ “Hello, world!” เดิมของเราอย่างไร กลับไปที่ directory projects ของคุณ (หรือที่ใดก็ตามที่ คุณตัดสินใจเก็บโค้ด) แล้วบนทุก OS รัน:
$ cargo new hello_cargo
$ cd hello_cargo
คำสั่งแรกสร้าง directory และโปรเจกต์ใหม่ชื่อ hello_cargo เราตั้งชื่อ โปรเจกต์เป็น hello_cargo และ Cargo สร้างไฟล์ของมันใน directory ชื่อ เดียวกัน
เข้าไปใน directory hello_cargo แล้วดูรายการไฟล์ คุณจะเห็นว่า Cargo generate สองไฟล์และหนึ่ง directory ให้: ไฟล์ Cargo.toml และ directory src ที่มีไฟล์ main.rs อยู่ข้างใน
มันยัง initialize Git repository ใหม่พร้อมไฟล์ .gitignore ด้วย ไฟล์ Git
จะไม่ถูก generate ถ้าคุณรัน cargo new ภายใน Git repository ที่มีอยู่
คุณ override พฤติกรรมนี้ได้โดยใช้ cargo new --vcs=git
หมายเหตุ: Git เป็น version control system ที่ใช้กันทั่วไป คุณสามารถ เปลี่ยน
cargo newให้ใช้ version control system อื่น หรือไม่ใช้เลย โดยใช้ flag--vcsรันcargo new --helpเพื่อดู option ที่มี
เปิด Cargo.toml ใน text editor ที่คุณเลือก มันควรหน้าตาคล้ายโค้ดใน Listing 1-2
[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2024"
[dependencies]
cargo newไฟล์นี้อยู่ในรูปแบบ TOML (Tom’s Obvious, Minimal Language) ซึ่งเป็นรูปแบบ configuration ของ Cargo
บรรทัดแรก [package] เป็น heading ของ section ที่บ่งบอกว่าบรรทัดที่ตาม
มาเป็นการ config package เมื่อเราเพิ่มข้อมูลในไฟล์นี้ เราจะเพิ่ม section
อื่น ๆ ด้วย
สามบรรทัดถัดมา set ข้อมูล config ที่ Cargo ต้องการสำหรับ compile โปรแกรม
ของคุณ: ชื่อ, version และ edition ของ Rust ที่จะใช้ เราจะพูดถึง key
edition ใน ภาคผนวก E
บรรทัดสุดท้าย [dependencies] คือจุดเริ่มต้นของ section ให้คุณ list
dependency ใด ๆ ของโปรเจกต์ ใน Rust เราเรียก package ของโค้ดว่า crate
เราไม่ต้องการ crate อื่นใดสำหรับโปรเจกต์นี้ แต่จะต้องใช้ใน project แรก
ในบทที่ 2 ดังนั้นเราจะใช้ section dependency ตอนนั้น
ทีนี้เปิด src/main.rs แล้วดู:
Filename: src/main.rs
fn main() {
println!("Hello, world!");
}
Cargo generate โปรแกรม “Hello, world!” ให้คุณแล้ว เหมือนที่เราเขียนใน Listing 1-1! ที่ผ่านมา ความแตกต่างระหว่างโปรเจกต์ของเราและโปรเจกต์ที่ Cargo generate ก็คือ Cargo วางโค้ดไว้ใน directory src และเรามีไฟล์ config Cargo.toml ใน directory บนสุด
Cargo คาดหวังว่าไฟล์ source ของคุณจะอยู่ภายใน directory src ส่วน directory โปรเจกต์ระดับบนสุดมีไว้สำหรับไฟล์ README, ข้อมูล license, ไฟล์ config และอื่น ๆ ที่ไม่เกี่ยวข้องกับโค้ด การใช้ Cargo ช่วยจัด ระเบียบโปรเจกต์ของคุณ มีที่สำหรับทุกอย่าง และทุกอย่างก็อยู่ที่ของมัน
ถ้าคุณเริ่มโปรเจกต์ที่ไม่ได้ใช้ Cargo อย่างที่เราทำกับโปรเจกต์
“Hello, world!” คุณสามารถแปลงมันเป็นโปรเจกต์ที่ใช้ Cargo ได้ ย้ายโค้ด
โปรเจกต์ไปไว้ใน directory src แล้วสร้างไฟล์ Cargo.toml ที่เหมาะสม
วิธีง่าย ๆ ในการได้ไฟล์ Cargo.toml นั้น คือรัน cargo init ซึ่งจะ
สร้างให้คุณอัตโนมัติ
Build และรันโปรเจกต์ Cargo
ทีนี้มาดูว่าอะไรต่างเมื่อเรา build และรันโปรแกรม “Hello, world!” ด้วย Cargo! จาก directory hello_cargo ของคุณ build โปรเจกต์โดยป้อนคำสั่ง ต่อไปนี้:
$ cargo build
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
Finished dev [unoptimized + debuginfo] target(s) in 2.85 secs
คำสั่งนี้สร้างไฟล์ executable ใน target/debug/hello_cargo (หรือ target\debug\hello_cargo.exe บน Windows) แทนที่จะอยู่ใน current directory ของคุณ เพราะ build default เป็น debug build Cargo จึงวาง binary ไว้ใน directory ชื่อ debug คุณรัน executable ได้ด้วยคำสั่งนี้:
$ ./target/debug/hello_cargo # หรือ .\target\debug\hello_cargo.exe บน Windows
Hello, world!
ถ้าทุกอย่างเรียบร้อย Hello, world! ควรพิมพ์ออก terminal การรัน
cargo build ครั้งแรกยังทำให้ Cargo สร้างไฟล์ใหม่ในระดับบนสุด:
Cargo.lock ไฟล์นี้ติดตาม version ที่แน่นอนของ dependency ในโปรเจกต์
ของคุณ โปรเจกต์นี้ไม่มี dependency ดังนั้นไฟล์จึงค่อนข้างว่าง คุณจะไม่
ต้องแก้ไฟล์นี้เอง Cargo จัดการเนื้อหาให้คุณ
เราเพิ่ง build โปรเจกต์ด้วย cargo build แล้วรันด้วย
./target/debug/hello_cargo แต่เราใช้ cargo run เพื่อ compile โค้ดแล้ว
รัน executable ที่ได้ในคำสั่งเดียวก็ได้:
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target/debug/hello_cargo`
Hello, world!
การใช้ cargo run สะดวกกว่าการต้องจำให้รัน cargo build แล้วใช้ path
ทั้งหมดของ binary ดังนั้นนักพัฒนาส่วนใหญ่ใช้ cargo run
สังเกตว่าครั้งนี้เราไม่เห็น output ที่บ่งบอกว่า Cargo กำลัง compile
hello_cargo Cargo รู้ว่าไฟล์ไม่เปลี่ยน ก็เลยไม่ rebuild แต่รัน binary
อย่างเดียว ถ้าคุณแก้ source code Cargo จะ rebuild โปรเจกต์ก่อนรัน และคุณ
จะเห็น output นี้:
$ cargo run
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
Finished dev [unoptimized + debuginfo] target(s) in 0.33 secs
Running `target/debug/hello_cargo`
Hello, world!
Cargo ยังมีคำสั่งชื่อ cargo check ด้วย คำสั่งนี้เช็คโค้ดอย่างรวดเร็ว
เพื่อให้แน่ใจว่ามัน compile ได้ แต่ไม่ produce executable:
$ cargo check
Checking hello_cargo v0.1.0 (file:///projects/hello_cargo)
Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs
ทำไมถึงไม่อยากได้ executable? บ่อยครั้ง cargo check เร็วกว่า
cargo build มาก เพราะข้ามขั้นตอน produce executable ถ้าคุณเช็คงานต่อ
เนื่องระหว่างเขียนโค้ด การใช้ cargo check จะเร่งกระบวนการให้คุณรู้ว่า
โปรเจกต์ยัง compile ผ่านหรือไม่! ด้วยเหตุนี้ Rustacean หลายคนจึงรัน
cargo check เป็นระยะระหว่างเขียนโปรแกรม เพื่อให้แน่ใจว่ามัน compile
ผ่าน แล้วค่อยรัน cargo build ตอนที่พร้อมใช้ executable
มาทบทวนสิ่งที่เราเรียนเกี่ยวกับ Cargo ที่ผ่านมา:
- เราสร้างโปรเจกต์ได้ด้วย
cargo new - เรา build โปรเจกต์ได้ด้วย
cargo build - เรา build และรันโปรเจกต์ในขั้นตอนเดียวได้ด้วย
cargo run - เรา build โปรเจกต์โดยไม่ produce binary เพื่อเช็ค error ได้ด้วย
cargo check - แทนที่จะบันทึกผลของ build ใน directory เดียวกับโค้ด Cargo เก็บมันไว้ ใน directory target/debug
ข้อดีเพิ่มเติมของการใช้ Cargo คือคำสั่งจะเหมือนกันไม่ว่าคุณทำงานบน OS อะไร ดังนั้นจากตรงนี้ เราจะไม่ให้คำแนะนำเฉพาะสำหรับ Linux และ macOS เทียบกับ Windows อีกต่อไป
Build เพื่อ Release
เมื่อโปรเจกต์ของคุณพร้อม release แล้ว คุณใช้ cargo build --release เพื่อ
compile มันพร้อม optimization ได้ คำสั่งนี้จะสร้าง executable ใน
target/release แทน target/debug การ optimize ทำให้โค้ด Rust รันเร็วขึ้น
แต่การเปิดมันใช้เวลา compile นานขึ้น นี่จึงเป็นเหตุผลที่มี profile สอง
แบบที่ต่างกัน: หนึ่งสำหรับ development ตอนที่คุณอยาก rebuild เร็วและบ่อย
อีกหนึ่งสำหรับการ build โปรแกรมสุดท้ายที่คุณจะส่งให้ user ซึ่งจะไม่ถูก
rebuild ซ้ำ ๆ และจะรันเร็วที่สุดเท่าที่ทำได้ ถ้าคุณ benchmark เวลารัน
ของโค้ด อย่าลืมรัน cargo build --release และ benchmark กับ executable
ใน target/release
ใช้ประโยชน์จาก convention ของ Cargo
สำหรับโปรเจกต์ง่าย ๆ Cargo ไม่ให้คุณค่าเพิ่มมากนักเหนือกว่าการใช้แค่
rustc แต่จะพิสูจน์คุณค่าเมื่อโปรแกรมของคุณซับซ้อนขึ้น เมื่อโปรแกรม
เติบโตเป็นหลายไฟล์หรือต้องการ dependency การให้ Cargo coordinate การ
build ก็ง่ายกว่ามาก
แม้โปรเจกต์ hello_cargo จะเรียบง่าย แต่ตอนนี้มันใช้ tooling ส่วนใหญ่ที่
คุณจะใช้จริงในส่วนที่เหลือของ Rust career ของคุณ จริง ๆ แล้ว เพื่อทำงาน
กับโปรเจกต์ที่มีอยู่ คุณใช้คำสั่งต่อไปนี้เพื่อ check out โค้ดด้วย Git
เปลี่ยนเข้า directory ของโปรเจกต์ แล้ว build ได้:
$ git clone example.org/someproject
$ cd someproject
$ cargo build
ดูข้อมูลเพิ่มเติมเกี่ยวกับ Cargo ที่ documentation ของมัน
สรุป
คุณเริ่มต้นการเดินทาง Rust ได้ดีมากแล้ว! ในบทนี้คุณได้เรียน:
- ติดตั้ง Rust version stable ล่าสุดด้วย
rustup - Update เป็น Rust version ใหม่กว่า
- เปิด documentation ที่ติดตั้งไว้ในเครื่อง
- เขียนและรันโปรแกรม “Hello, world!” โดยใช้
rustcตรง ๆ - สร้างและรันโปรเจกต์ใหม่โดยใช้ convention ของ Cargo
นี่เป็นเวลาที่ดีที่จะ build โปรแกรมที่ใหญ่ขึ้น เพื่อทำความคุ้นเคยกับการ อ่านและเขียนโค้ด Rust ดังนั้น ในบทที่ 2 เราจะ build โปรแกรมเกมทายตัวเลข ถ้าคุณอยากเริ่มด้วยการเรียนว่าแนวคิดพื้นฐานของการเขียนโปรแกรมทำงานยังไง ใน Rust ดูบทที่ 3 แล้วค่อยกลับมาที่บทที่ 2