Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

รับ Command Line Argument

มาสร้างโปรเจกต์ใหม่ด้วย cargo new เหมือนเคย เราจะตั้งชื่อโปรเจกต์ของ เราว่า minigrep เพื่อแยกจากเครื่องมือ grep ที่คุณอาจมีในระบบแล้ว:

$ cargo new minigrep
     Created binary (application) `minigrep` project
$ cd minigrep

งานแรกคือทำให้ minigrep รับสองอาร์กิวเมนต์ command line — file path และ string ที่จะค้นหา นั่นคือ เราต้องการรันโปรแกรมของเราด้วย cargo run, hyphen สองตัวเพื่อระบุว่าอาร์กิวเมนต์ต่อไปนี้สำหรับโปรแกรมของเราไม่ใช่ สำหรับ cargo, string ที่จะค้นหา และ path ของไฟล์ที่จะค้นหาใน แบบนี้:

$ cargo run -- searchstring example-filename.txt

ตอนนี้ โปรแกรมที่ cargo new สร้างประมวลผลอาร์กิวเมนต์ที่เราให้มันไม่ ได้ บาง library ที่มีอยู่บน crates.io ช่วยใน การเขียนโปรแกรมที่รับอาร์กิวเมนต์ command line ได้ แต่เพราะคุณกำลัง เรียนแนวคิดนี้ มา implement ความสามารถนี้ด้วยตัวเองกัน

อ่านค่า Argument

เพื่อให้ minigrep อ่านค่าของอาร์กิวเมนต์ command line ที่เราส่งให้ได้ เราจะต้องการฟังก์ชัน std::env::args ที่ standard library ของ Rust ให้มา ฟังก์ชันนี้ return iterator ของอาร์กิวเมนต์ command line ที่ ส่งให้ minigrep เราจะครอบคลุม iterator ทั้งหมดใน บทที่ 13 ตอนนี้ คุณต้องรู้แค่สองรายละเอียด เกี่ยวกับ iterator — iterator สร้างชุดของค่า และเราเรียกเมธอด collect บน iterator เพื่อเปลี่ยนให้เป็น collection เช่น vector ซึ่งบรรจุ element ทั้งหมดที่ iterator สร้างได้

โค้ดใน Listing 12-1 ทำให้โปรแกรม minigrep ของคุณอ่านอาร์กิวเมนต์ command line ใด ๆ ที่ส่งให้ และ collect ค่าเข้า vector

Filename: src/main.rs
use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    dbg!(args);
}
Listing 12-1: collect อาร์กิวเมนต์ command line เข้า vector และ print พวกมัน

ก่อนอื่น เรานำโมดูล std::env เข้า scope ด้วย statement use เพื่อให้ เราใช้ฟังก์ชัน args ของมันได้ สังเกตว่าฟังก์ชัน std::env::args ถูก ซ้อนในโมดูลสองระดับ ดังที่เราพูดถึงใน บทที่ 7 ในกรณีที่ฟังก์ชันที่ต้องการ ถูกซ้อนในมากกว่าหนึ่งโมดูล เราเลือกนำโมดูล parent เข้า scope แทน ฟังก์ชัน เมื่อทำเช่นนั้น เราใช้ฟังก์ชันอื่นจาก std::env ได้ง่าย ๆ มันยังกำกวมน้อยกว่าการเพิ่ม use std::env::args แล้วเรียกฟังก์ชันด้วย แค่ args เพราะ args อาจถูกเข้าใจผิดง่าย ๆ ว่าเป็นฟังก์ชันที่นิยาม ในโมดูลปัจจุบัน

ฟังก์ชัน args และ Unicode ที่ไม่ valid

สังเกตว่า std::env::args จะ panic ถ้าอาร์กิวเมนต์ใดมี Unicode ที่ ไม่ valid ถ้าโปรแกรมของคุณต้องรับอาร์กิวเมนต์ที่มี Unicode ที่ไม่ valid ใช้ std::env::args_os แทน ฟังก์ชันนั้น return iterator ที่ สร้างค่า OsString แทนค่า String เราเลือกใช้ std::env::args ที่นี่เพื่อความง่ายเพราะค่า OsString ต่างกันตามแต่ละ platform และ ทำงานด้วยซับซ้อนกว่าค่า String

ในบรรทัดแรกของ main เราเรียก env::args และเราใช้ collect ทันที เพื่อเปลี่ยน iterator เป็น vector ที่บรรจุค่าทั้งหมดที่ iterator สร้าง เราใช้ฟังก์ชัน collect เพื่อสร้าง collection หลายประเภทได้ ดังนั้น เราระบุ type ของ args อย่างชัดเจนเพื่อระบุว่าเราต้องการ vector ของ string แม้คุณจะแทบไม่ต้อง annotate type ใน Rust collect เป็นฟังก์ชัน หนึ่งที่คุณมักต้อง annotate เพราะ Rust ไม่สามารถ infer ประเภทของ collection ที่คุณต้องการได้

สุดท้าย เรา print vector โดยใช้ debug macro มาลองรันโค้ดก่อน — ก่อน อื่นโดยไม่มีอาร์กิวเมนต์ จากนั้นด้วยสองอาร์กิวเมนต์:

$ cargo run
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.61s
     Running `target/debug/minigrep`
[src/main.rs:5:5] args = [
    "target/debug/minigrep",
]
$ cargo run -- needle haystack
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.57s
     Running `target/debug/minigrep needle haystack`
[src/main.rs:5:5] args = [
    "target/debug/minigrep",
    "needle",
    "haystack",
]

สังเกตว่าค่าแรกใน vector คือ "target/debug/minigrep" ซึ่งเป็นชื่อของ binary ของเรา นี่ตรงกับพฤติกรรมของ list อาร์กิวเมนต์ใน C ที่ให้ โปรแกรมใช้ชื่อที่ใช้เรียกพวกมันใน execution ของพวกมัน มันมักสะดวกที่ จะมีสิทธิ์เข้าถึงชื่อโปรแกรมในกรณีที่คุณต้องการ print มันในข้อความ หรือเปลี่ยนพฤติกรรมของโปรแกรมตาม alias command line ที่ใช้เรียกโปรแกรม แต่สำหรับจุดประสงค์ของบทนี้ เราจะ ignore มันและบันทึกเฉพาะสองอาร์กิวเมนต์ ที่เราต้องการ

บันทึกค่า Argument ในตัวแปร

ตอนนี้โปรแกรมเข้าถึงค่าที่ระบุเป็นอาร์กิวเมนต์ command line ได้ ตอนนี้ เราต้องบันทึกค่าของสองอาร์กิวเมนต์ในตัวแปรเพื่อให้เราใช้ค่าตลอดส่วนที่ เหลือของโปรแกรมได้ เราทำสิ่งนั้นใน Listing 12-2

Filename: src/main.rs
use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();

    let query = &args[1];
    let file_path = &args[2];

    println!("Searching for {query}");
    println!("In file {file_path}");
}
Listing 12-2: สร้างตัวแปรเพื่อเก็บอาร์กิวเมนต์ query และอาร์กิวเมนต์ file path

ดังที่เราเห็นเมื่อเรา print vector ชื่อของโปรแกรมใช้ค่าแรกใน vector ที่ args[0] ดังนั้นเราเริ่มอาร์กิวเมนต์ที่ index 1 อาร์กิวเมนต์แรก ที่ minigrep รับคือ string ที่เรากำลังค้นหา ดังนั้นเราวาง reference ของอาร์กิวเมนต์แรกในตัวแปร query อาร์กิวเมนต์ที่สองจะเป็น file path ดังนั้นเราวาง reference ของอาร์กิวเมนต์ที่สองในตัวแปร file_path

เรา print ค่าของตัวแปรเหล่านี้ชั่วคราวเพื่อพิสูจน์ว่าโค้ดทำงานตามที่ เราตั้งใจ มารันโปรแกรมนี้อีกครั้งด้วยอาร์กิวเมนต์ test และ sample.txt:

$ cargo run -- test sample.txt
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.0s
     Running `target/debug/minigrep test sample.txt`
Searching for test
In file sample.txt

ดี โปรแกรมทำงาน! ค่าของอาร์กิวเมนต์ที่เราต้องการกำลังถูกบันทึกในตัวแปร ที่ถูกต้อง ภายหลังเราจะเพิ่มการจัดการ error เพื่อจัดการกับสถานการณ์ ที่อาจผิดพลาดบางอย่าง เช่นเมื่อ user ไม่ให้อาร์กิวเมนต์ ตอนนี้เราจะ ignore สถานการณ์นั้นและทำงานเพิ่มความสามารถในการอ่านไฟล์แทน