#9512 - Rewrite It in Rust
- เชลล์ Fish ถูกเขียนใหม่ด้วย Rust โดยไม่มีโค้ด C++ เหลืออยู่เลย และประกอบด้วย Rust แบบเพียวเกือบ 100%
- มีการเปิด PR (#9512) เพื่อย้าย Fish จาก C++ ไปเป็น Rust ตั้งแต่ราว 2 ปีก่อน
- แม้ว่า Fish จะเคยย้ายจาก C ไป C++ มาก่อน แต่การย้ายมาเป็น Rust เป็นโครงการที่ใหญ่กว่ามาก
ปัญหาใน C++
- ความต่างของเครื่องมือและคอมไพเลอร์: เครื่องมือของ C++ ไม่ค่อยดีนัก และการรองรับมาตรฐาน C++ รุ่นใหม่ทำให้ผู้แพ็กเกจและผู้ร่วมพัฒนาต้องรับความซับซ้อนเพิ่มขึ้น
- ความปลอดภัยของเธรด: การรันคำสั่งภายในของ Fish ปัจจุบันยังเป็นแบบลำดับต่อเนื่อง และหากต้องการเพิ่มพรอมป์ต์แบบอะซิงก์หรือระบบเติมคำสั่งแบบไม่บล็อก ก็จำเป็นต้องรองรับการประมวลผลแบบขนาน
- ความซับซ้อนของภาษา: header file, template และการจัดการสตริงของ C++ มีความซับซ้อนและไม่ปลอดภัย
- ชุมชน: C++ ไม่สามารถดึงดูดผู้ร่วมพัฒนาได้มากนัก
- ปัญหาเรื่อง dependency: มีความยุ่งยากจากความไม่เสถียรและปัญหาการบิลด์ของไลบรารี C บางตัว (
curses)
เหตุผลที่เลือก Rust
- ความสนุกและน่าสนใจ: Fish เป็นโปรเจกต์งานอดิเรก จึงต้องการภาษาที่สนุกและน่าสนใจ และ Rust ก็มีเสน่ห์ต่อผู้ร่วมพัฒนามากกว่า
- เครื่องมือที่ยอดเยี่ยม: ติดตั้งคอมไพเลอร์ได้ง่ายด้วย
rustup และข้อความแจ้งข้อผิดพลาดก็ชัดเจน
- Ergonomics: มีระบบ
use ที่ชัดเจน และมีฟีเจอร์ที่ปลอดภัยอย่าง Option และ Result
- การออกแบบภาษาที่ดี: ระบบ pointer และ option ของ Rust ปลอดภัยกว่า C++ มาก
- รองรับการประมวลผลแบบขนาน:
Send และ Sync ของ Rust ทำให้การประมวลผลแบบขนานที่ปลอดภัยเป็นไปได้
- การจัดการ dependency: สามารถเพิ่มการรองรับฟอร์แมตภายนอกอย่าง YAML, JSON เป็นต้น ได้อย่างง่ายดาย
การรองรับแพลตฟอร์ม
- รองรับแพลตฟอร์มหลักส่วนใหญ่ เช่น macOS, Linux, BSD และอื่น ๆ โดยไม่ได้ตั้งเป้ารองรับ Windows แบบเนทีฟ
- Fish เป็นเชลล์ที่เน้น UNIX เป็นหลัก จึงโฟกัสที่ UNIX API และภาษาสคริปต์มากกว่าสภาพแวดล้อม Windows
กระบวนการพอร์ต
- Fish ย้ายจาก C++ ไป Rust แบบค่อยเป็นค่อยไปตามแนวคิด “ปลาแห่งเธซีอุส” โดยย้ายคอมโพเนนต์ทีละตัวและจัดให้ C++ กับ Rust อยู่ร่วมกันได้
- เรือของเธซีอุส (Ship of Theseus): “ถ้าเปลี่ยนไม้กระดานทุกแผ่นของเรือลำหนึ่งเป็นของใหม่ทั้งหมด มันยังเป็นเรือลำเดิมอยู่หรือไม่?”
- ใช้ FFI: ใช้ autocxx เพื่อสร้าง binding ระหว่าง C++ และ Rust แล้วพอร์ตทีละคอมโพเนนต์
- พอร์ตครั้งใหญ่: บางส่วนเฉพาะทาง (เช่น การจัดการ I/O) ถูกย้ายแบบแยกเดี่ยวเพื่อลดโค้ด FFI ที่ซับซ้อน
- ปรับปรุงเครื่องมือ: ในระหว่างการพอร์ต มีการปรับแต่ง
autocxx เพื่อแก้ปัญหาการทำงานร่วมกันระหว่าง Rust กับ C++
ไทม์ไลน์
- มกราคม 2023: เปิด PR แรก
- มกราคม 2024: ลบโค้ด C++ ออกทั้งหมด
- ธันวาคม 2024: ออกรุ่นเบตา Fish 4.0
จุดที่ติดขัดกับ Rust
- ปัญหาด้าน portability: แนวทาง
#[cfg(...)] ของ Rust ไม่มีประสิทธิภาพนักเมื่อต้องจัดการความแตกต่างของระบบในระดับล่าง
- การแปลภาษา: format string ของ Rust ถูกตรวจสอบตอนคอมไพล์ แต่ไม่เอื้อต่อการแปล
- เวลาบิลด์: การใช้ LTO และการบิลด์แบบ release ค่าเริ่มต้นอาจทำให้เวลาบิลด์นาน
- ระหว่างการพอร์ตมีข้อผิดพลาดอยู่บ้าง แต่ส่วนใหญ่แก้ได้ไม่ยาก
ผลลัพธ์สำคัญ
- ถอด
curses ออก: แทนที่ฐานข้อมูล terminfo ด้วย Rust crate เพื่อแก้ปัญหา global state และปัญหาการบิลด์
- ไฟล์ปฏิบัติการเดี่ยว: สามารถสร้างไบนารี Fish ที่รวม dependency ทั้งหมดไว้ภายในได้
- ทำให้แพ็กเกจ Fish ติดตั้งได้ด้วยตัวเอง ผู้ใช้จึงใช้งานได้สะดวกขึ้น
- ประสิทธิภาพดีขึ้น: ปรับการใช้หน่วยความจำให้เหมาะสมขึ้น และเพิ่มฟีเจอร์ใหม่ได้ง่ายขึ้น
ข้อจำกัด
- ยังไม่สามารถเอา CMake ออกได้ทั้งหมด
- ยุติการรองรับ Cygwin: เพราะไม่มี Rust target
- บน Windows ยังรันได้ผ่าน WSL เท่านั้น
ปัจจุบันและอนาคต
- Fish 4.0 พอร์ตเสร็จสมบูรณ์แล้วและมีประสิทธิภาพดีขึ้น
- Fish ยังเป็น UNIX shell เช่นเดิม แต่การย้ายไป Rust ทำให้เพิ่มฟีเจอร์ใหม่ได้มากขึ้น
- ตอนนี้มีโค้ดเบสที่ย้ายมาเป็น Rust อย่างสมบูรณ์แล้ว ทำให้ดูแลรักษาและเพิ่มฟีเจอร์ได้ง่ายกว่าเดิม และสามารถใช้ข้อดีของ Rust เพื่อเพิ่มความสามารถใหม่ ๆ ได้
- การย้ายครั้งนี้เสร็จสมบูรณ์อย่างประสบความสำเร็จ และส่งผลในทางบวกทั้งต่อผู้ร่วมพัฒนาและผู้ใช้
3 ความคิดเห็น
ผมอิจฉาความใช้งานง่ายของ fish แต่เพราะปัญหาเรื่องความเข้ากันได้ ประสิทธิภาพ ฯลฯ เลยตั้งค่า zsh ให้คล้าย fish มากที่สุดแล้วใช้งานอยู่ ตอนนี้เลยตั้งตารอดูว่า fish ที่เปลี่ยนไปจะเป็นยังไง 👀
เชลล์อินเทอร์แอคทีฟที่ใช้งานเป็นมิตร - Fish
ความคิดเห็นบน Hacker News
ขอแสดงความยินดีกับทีม Fish และรายละเอียดของโปรเจ็กต์ก็น่าสนใจ สงสัยว่านี่อาจเป็นโปรเจ็กต์ที่ใหญ่ที่สุดที่ย้ายจาก C++ ไปเป็น Rust อย่างสมบูรณ์หรือไม่ ซึ่งอาจเป็นบทเรียนที่มีประโยชน์ต่อโปรเจ็กต์อื่น
ข้อบ่นหลักเกี่ยวกับ Rust คือการรองรับการตรวจจับเวอร์ชัน โดยการตรวจจับความสามารถเหมาะกว่าทั้งกับดิสโทร เว็บเบราว์เซอร์ และคอมไพเลอร์
หนึ่งในเป้าหมายของการพอร์ตคือการเอา CMake ออก แต่ไม่สำเร็จ Cargo ยอดเยี่ยมสำหรับการบิลด์ แต่เรียบง่ายเกินไปสำหรับการติดตั้ง Fish มีทั้งสคริปต์และเอกสารจำนวนมาก จึงไม่เข้ากับกรณีใช้งานของ Cargo
เมื่อหลายปีก่อนเคยย้ายจาก bash ไป zsh แล้วก็พอใจ แต่พอลองใช้ fish บนคอมพิวเตอร์เครื่องใหม่กลับรู้สึกว่า zsh ทั้งยุ่งยากและล้าสมัย แนะนำให้ลองใช้ fish สักสองสามสัปดาห์
น่าเสียดายที่ไม่รองรับ Cygwin และหวังว่า Rust จะรองรับ Cygwin เป็นเป้าหมายการบิลด์
ประทับใจกับความพยายามของทีม Fish และรอดูว่าโปรเจ็กต์จะพัฒนาต่อไปอย่างไร
สงสัยว่าผู้แพ็กเกจของดิสโทรจะสามารถแพ็กเกจ Rust-fish ตามแนวทางของ Debian ได้สะดวกแค่ไหน
ขอแสดงความยินดีกับทีม Fish และคิดว่าเชลล์ที่ดีที่สุดยิ่งดีขึ้นไปอีก พร้อมเสนอให้อัปเดตแท็กไลน์ของโปรเจ็กต์เป็น "Finally, a shell for the 00s!"
หลังจากย้ายจาก zsh มา Fish การตั้งค่าง่ายขึ้นมาก และ Fish ก็ทำงานตรงตามที่คาดหวัง จึงไม่คิดจะเปลี่ยนกลับอีก
มาโคร
cfg!จะถูกคอมไพล์เป็น true/false ดังนั้นโค้ดภายในifguard จึงต้องสามารถคอมไพล์ได้ และอาจล้มเหลวได้หากคอมไพล์โดยไม่มีmy_featureFish ใช้เธรดสำหรับการเติมคำสั่งอัตโนมัติและการเน้นไวยากรณ์ และยังมีโปรเจ็กต์ระยะยาวเพื่อเพิ่มความสามารถด้าน concurrency ให้กับภาษา