การใช้ Rust เมื่อเริ่มต้น: เรื่องเตือนใจ

Nov 25 2022
สนิมนั้นยอดเยี่ยมสำหรับบางสิ่ง แต่คิดให้ดีก่อนที่จะหยิบมันขึ้นมาสำหรับสตาร์ทอัพที่ต้องก้าวไปอย่างรวดเร็ว

สนิมนั้นยอดเยี่ยมสำหรับบางสิ่ง แต่คิดให้ดีก่อนที่จะหยิบมันขึ้นมาสำหรับสตาร์ทอัพที่ต้องก้าวไปอย่างรวดเร็ว

ศิลปะทั้งหมดสำหรับโพสต์นี้สร้างขึ้นโดยใช้ DALL-E

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

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

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

ประสบการณ์หลักของฉันจาก Rust มาจากการทำงานกับมันมากกว่า 2 ปีในการเริ่มต้นครั้งก่อน โปรเจ็กต์นี้เป็นผลิตภัณฑ์ SaaS บนคลาวด์ที่เป็นแอป CRUD ทั่วไปไม่มากก็น้อย: เป็นชุดของไมโครเซอร์วิสที่ให้จุดสิ้นสุด REST และ gRPC API ที่ด้านหน้าของฐานข้อมูล รวมถึงแบ็คพอยท์อื่น ๆ จบ microservices (ตัวมันเองถูกนำไปใช้ในการรวมกันของ Rust และ Python) สนิมถูกใช้เป็นหลักเนื่องจากผู้ก่อตั้งบริษัทสองคนเป็นผู้เชี่ยวชาญด้านสนิม เมื่อเวลาผ่านไป เราได้ขยายทีมอย่างมาก (เพิ่มจำนวนพนักงานด้านวิศวกรรมเกือบ 10 เท่า) และขนาดและความซับซ้อนของ codebase ก็เพิ่มขึ้นอย่างมากเช่นกัน

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

สนิมควรเป็นสิ่งที่ดีที่สุดตั้งแต่ขนมปังหั่นบาง ๆ แล้วทำไมมันถึงไม่ได้ผลดีสำหรับเรา?

สนิมมีช่วงการเรียนรู้ที่ยิ่งใหญ่

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

แน่นอนว่าแนวคิด "ใหม่" เหล่านั้นบางส่วนมีอยู่ในภาษาอื่น — โดยเฉพาะอย่างยิ่งแนวคิดที่ใช้งานได้ — แต่ Rust นำแนวคิดเหล่านี้เข้าสู่การตั้งค่าภาษา "กระแสหลัก" และด้วยเหตุนี้จึงเป็นเรื่องใหม่สำหรับผู้มาใหม่จำนวนมากของ Rust

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

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

มีวิธีอื่นในการแก้ไขปัญหาที่ Rust พยายามแก้ไข

ดังที่กล่าวไว้ข้างต้น บริการที่เรากำลังสร้างคือแอป CRUD ที่ค่อนข้างตรงไปตรงมา โหลดที่คาดไว้สำหรับบริการนี้จะไม่เกินสองสามข้อความค้นหาต่อวินาที สูงสุดตลอดอายุการใช้งานของระบบนี้ บริการนี้เป็นส่วนหน้าของไปป์ไลน์การประมวลผลข้อมูลที่ค่อนข้างซับซ้อนซึ่งอาจใช้เวลาหลายชั่วโมงในการทำงาน ดังนั้นตัวบริการเองจึงไม่คาดว่าจะเกิดปัญหาคอขวดด้านประสิทธิภาพ ไม่มีความกังวลเป็นพิเศษว่าภาษาทั่วไปเช่น Python จะมีปัญหาในการแสดงประสิทธิภาพที่ดี ไม่มีความต้องการพิเศษด้านความปลอดภัยหรือการทำงานพร้อมกันนอกเหนือจากที่บริการ Web-Facing จำเป็นต้องจัดการ เหตุผลเดียวที่เราใช้ Rust ก็เพราะผู้เขียนดั้งเดิมของระบบเป็นผู้เชี่ยวชาญด้านสนิม ไม่ใช่เพราะมันเหมาะสมอย่างยิ่งสำหรับการสร้างบริการประเภทนี้

Rust ได้ตัดสินใจว่าความปลอดภัยมีความสำคัญมากกว่าประสิทธิภาพของนักพัฒนา นี่เป็นการแลกเปลี่ยนที่ถูกต้องในหลาย ๆ สถานการณ์ — เช่น การสร้างโค้ดในเคอร์เนล OS หรือสำหรับระบบฝังตัวที่มีข้อจำกัดของหน่วยความจำ — แต่ฉันไม่คิดว่ามันเป็นการแลกเปลี่ยนที่ถูกต้องในทุกกรณี โดยเฉพาะอย่างยิ่งไม่ใช่ในการเริ่มต้นที่ความเร็วเป็นสิ่งสำคัญ ฉันเป็นนักปฏิบัติ ฉันอยากให้ทีมเสียเวลาไปกับการแก้จุดบกพร่องของหน่วยความจำรั่วหรือข้อผิดพลาดประเภทในบางครั้งสำหรับโค้ดที่เขียน เช่น Python หรือ Go ดีกว่าให้ทุกคนในทีมได้รับผลกระทบด้านประสิทธิภาพการทำงาน 4 เท่าจากการใช้ภาษาที่ออกแบบมาเพื่อหลีกเลี่ยงปัญหาเหล่านี้โดยสิ้นเชิง .

ดังที่ฉันได้กล่าวไว้ข้างต้น ทีมงานของฉันที่ Google ได้สร้างบริการทั้งหมดใน Go ซึ่งเมื่อเวลาผ่านไปก็รองรับผู้ใช้มากกว่า 800 ล้านคน และ QPS ของ Google Search เพิ่มขึ้นถึง 4 เท่าเมื่อถึงจุดสูงสุด ฉันสามารถนับจำนวนครั้งที่เราพบปัญหาที่เกิดจากระบบประเภท Go หรือตัวเก็บขยะในช่วงหลายปีที่สร้างและใช้บริการนี้ โดยพื้นฐานแล้วปัญหาที่สนิมได้รับการออกแบบให้หลีกเลี่ยงสามารถแก้ไขได้ด้วยวิธีอื่นๆ — โดยการทดสอบที่ดี การหลุดร่อนที่ดี การตรวจสอบโค้ดที่ดี และการตรวจสอบที่ดี แน่นอนว่าไม่ใช่ทุกโครงการซอฟต์แวร์ที่มีความหรูหราเช่นนี้ ดังนั้นฉันจึงจินตนาการได้ว่า Rust อาจเป็นตัวเลือกที่ดีในสถานการณ์อื่นๆ เหล่านั้น

คุณจะต้องลำบากในการจ้างนักพัฒนา Rust

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

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

ไลบรารีและเอกสารยังไม่บรรลุนิติภาวะ

นี่เป็นปัญหาที่ (ฉันหวังว่า!) จะได้รับการแก้ไขเมื่อเวลาผ่านไป แต่เมื่อเทียบกับ Go แล้ว ไลบรารี่และระบบนิเวศเอกสารของ Rust นั้นยังไม่บรรลุนิติภาวะอย่างไม่น่าเชื่อ ตอนนี้ Go ได้รับประโยชน์จากการได้รับการพัฒนาและสนับสนุนโดยทีมงานที่ทุ่มเทของ Google ก่อนที่จะเผยแพร่สู่สายตาชาวโลก เอกสารและไลบรารีจึงได้รับการขัดเกลาพอสมควร โดยการเปรียบเทียบสนิมให้ความรู้สึกเหมือนอยู่ในความคืบหน้ามานานแล้ว เอกสารสำหรับไลบรารียอดนิยมจำนวนมากค่อนข้างกระจัดกระจาย และเรามักจะต้องอ่านซอร์สโค้ดของไลบรารีที่ระบุเพื่อทำความเข้าใจวิธีใช้งาน นี้ไม่ดี.

นักขอโทษจาก Rust ในทีมมักจะพูดว่า "async/await ยังใหม่อยู่" และ "ใช่ เอกสารสำหรับไลบรารีนั้นยังขาดอยู่" แต่ข้อบกพร่องเหล่านี้ส่งผลกระทบต่อทีมค่อนข้างมาก เราทำผิดพลาดครั้งใหญ่ตั้งแต่เนิ่นๆ ด้วยการนำ Actix มาใช้เป็นเว็บเฟรมเวิร์กสำหรับบริการของเรา ซึ่งเป็นการตัดสินใจที่นำไปสู่ความเจ็บปวดและความทุกข์ทรมานอย่างมากมายเมื่อเราพบข้อบกพร่องและปัญหาที่ฝังลึกอยู่ในไลบรารีซึ่งไม่มีใครสามารถหาวิธีแก้ไขได้ (พูดตามตรง เมื่อหลายปีก่อนและตอนนี้อาจดีขึ้นแล้ว)

แน่นอนว่าความยังไม่บรรลุนิติภาวะแบบนี้ไม่ได้เจาะจงกับ Rust แต่นั่นก็เท่ากับภาษีที่ทีมของคุณต้องจ่าย ไม่ว่าเอกสารและแบบฝึกสอนภาษาหลักจะดีเพียงใด หากคุณไม่สามารถหาวิธีใช้ไลบรารีได้ ก็ไม่สำคัญเท่าไหร่ (เว้นแต่ว่าคุณวางแผนที่จะเขียนทุกอย่างตั้งแต่เริ่มต้น)

สนิมทำให้คุณสมบัติใหม่หยาบมาก

ฉันไม่รู้เกี่ยวกับใครเลย แต่อย่างน้อยสำหรับฉัน เมื่อฉันสร้างฟีเจอร์ใหม่ ฉันมักจะไม่มีประเภทข้อมูลทั้งหมด, API และรายละเอียดปลีกย่อยอื่น ๆ ที่เตรียมไว้ล่วงหน้า ฉันมักจะผายลมโค้ดออกมาเพื่อให้แนวคิดพื้นฐานบางอย่างทำงาน และตรวจสอบว่าสมมติฐานของฉันเกี่ยวกับวิธีการทำงานของสิ่งต่างๆ นั้นถูกต้องไม่มากก็น้อย การทำสิ่งนี้ใน Python นั้นง่ายมาก เพราะคุณสามารถเล่นได้อย่างรวดเร็วและหลวมกับสิ่งต่างๆ เช่น การพิมพ์ และไม่ต้องกังวลหากเส้นทางของโค้ดบางเส้นทางเสียหายในขณะที่คุณคิดคร่าวๆ คุณสามารถย้อนกลับได้ในภายหลังและทำให้เป็นระเบียบเรียบร้อยและแก้ไขข้อผิดพลาดประเภททั้งหมดและเขียนแบบทดสอบทั้งหมด

ใน Rust การ “เขียนโค้ดแบบร่าง” แบบนี้เป็นเรื่องยากมาก เพราะคอมไพเลอร์สามารถและจะบ่นเกี่ยวกับทุกสิ่งที่ไม่ผ่านการตรวจสอบประเภทและอายุ การใช้งาน — เนื่องจากมันถูกออกแบบมาอย่างชัดเจนให้ทำ สิ่งนี้เหมาะสมอย่างยิ่งเมื่อคุณต้องการสร้างการใช้งานขั้นสุดท้ายพร้อมการผลิต แต่น่าเสียดายอย่างยิ่งเมื่อคุณพยายามคุ้ยเขี่ยบางอย่างร่วมกันเพื่อทดสอบแนวคิดหรือหาพื้นฐานพื้นฐาน มาโคร มีunimplemented!ประโยชน์ในระดับหนึ่ง แต่ก็ยังต้องการให้ทุกอย่างตรวจสอบประเภทขึ้นและลงสแต็กก่อนที่คุณจะสามารถคอมไพล์ได้

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

Rust เก่งอะไร?

มีหลายอย่างที่ฉันชอบเกี่ยวกับ Rust และฟีเจอร์จาก Rust ที่ฉันอยากได้ในภาษาอื่นๆ ไวยากรณ์matchดีมาก คุณลักษณะOption, ResultและErrorมีประสิทธิภาพมาก และตัว?ดำเนินการก็เป็นวิธีการจัดการข้อผิดพลาดที่สวยงาม แนวคิดเหล่านี้จำนวนมากมีคู่ในภาษาอื่น แต่แนวทางของ Rust กับแนวคิดเหล่านี้นั้นสวยงามเป็นพิเศษ

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

เอาล่ะ ตอนนี้ฉันโกรธผู้อ่าน Hacker News ไปพอสมควรแล้ว ฉันเดาว่าตอนนี้เป็นเวลาที่ดีที่จะประกาศหัวข้อของบทความถัดไปของฉัน: เหตุใดจึงnanoเป็นโปรแกรมแก้ไขข้อความที่ยอดเยี่ยม เจอกันคราวหน้า!