เรื่องการทำงานพร้อมกันที่ดีกว่านั้นเป็นหนึ่งในเป้าหมายหลักของโครงการ Rust ดังนั้นควรมีการปรับปรุงหากเราเชื่อมั่นในโครงการเพื่อให้บรรลุเป้าหมาย ข้อจำกัดความรับผิดชอบเต็มรูปแบบ: ฉันมีความคิดเห็นสูงเกี่ยวกับสนิมและฉันลงทุนกับมัน ตามที่ร้องขอผมจะพยายามที่จะหลีกเลี่ยงการตัดสินคุณค่าและอธิบายความแตกต่างมากกว่า (IMHO) การปรับปรุง
สนิมปลอดภัยและไม่ปลอดภัย
"สนิม" ประกอบด้วยสองภาษา: ภาษาที่พยายามแยกคุณออกจากอันตรายของการเขียนโปรแกรมระบบและภาษาที่มีประสิทธิภาพยิ่งกว่าโดยไม่มีแรงบันดาลใจใด ๆ
Unsafe Rust เป็นภาษาที่น่ารังเกียจและโหดร้ายที่ให้ความรู้สึกเหมือน C ++ ช่วยให้คุณทำสิ่งที่เป็นอันตรายโดยพลการพูดคุยกับฮาร์ดแวร์จัดการข้อผิดพลาดด้วยตนเองถ่ายภาพด้วยตนเองและอื่น ๆ มันเหมือนกับ C และ C ++ ที่ความถูกต้องของโปรแกรมอยู่ในมือคุณ และมือของโปรแกรมเมอร์อื่น ๆ ที่เกี่ยวข้องทั้งหมด คุณเลือกใช้ภาษานี้โดยใช้คำหลักunsafe
และใน C และ C ++ ความผิดพลาดเพียงครั้งเดียวในสถานที่เดียวสามารถทำให้โครงการทั้งหมดพัง
Safe Rust คือ "ค่าเริ่มต้น" รหัสสนิมส่วนใหญ่มีความปลอดภัยและหากคุณไม่เคยพูดถึงคำหลักunsafe
ในรหัสของคุณคุณจะไม่ทิ้งภาษาที่ปลอดภัยไว้ ส่วนที่เหลือของการโพสต์ส่วนใหญ่จะเกี่ยวข้องกับภาษานั้นเพราะunsafe
รหัสสามารถทำลายการรับประกันใด ๆ และทั้งหมดที่สนิมปลอดภัยทำงานอย่างหนักเพื่อให้คุณ ในทางกลับกันunsafe
โค้ดไม่ได้ชั่วร้ายและไม่ได้รับการปฏิบัติเช่นนี้โดยชุมชน (อย่างไรก็ตามเป็นสิ่งที่ท้อแท้อย่างยิ่งเมื่อไม่จำเป็น)
มันอันตรายใช่ แต่ก็มีความสำคัญเช่นกันเพราะช่วยให้สามารถสร้าง abstractions ที่รหัสที่ปลอดภัยใช้ รหัสที่ไม่ปลอดภัยที่ดีใช้ระบบประเภทเพื่อป้องกันไม่ให้ผู้อื่นใช้ในทางที่ผิดดังนั้นการมีรหัสที่ไม่ปลอดภัยในโปรแกรม Rust ไม่จำเป็นต้องรบกวนรหัสที่ปลอดภัย ความแตกต่างต่อไปนี้มีอยู่ทั้งหมดเนื่องจากระบบประเภทของ Rust มีเครื่องมือที่ C ++ ไม่มีและเนื่องจากรหัสที่ไม่ปลอดภัยซึ่งใช้ abstractcurrency พร้อมกันจะใช้เครื่องมือเหล่านี้อย่างมีประสิทธิภาพ
ไม่แตกต่าง: หน่วยความจำแบบแบ่งใช้ / ไม่แน่นอน
แม้ว่า Rust จะให้ความสำคัญกับการส่งข้อความมากขึ้นและควบคุมหน่วยความจำที่ใช้ร่วมกันอย่างเข้มงวด แต่ก็ไม่ได้ตัดทอนการทำงานพร้อมกันของหน่วยความจำที่ใช้ร่วมกันและสนับสนุน abstractions ทั่วไปอย่างชัดเจน (ล็อค, การทำงานของอะตอม
ยิ่งกว่านั้นเช่น C ++ และแตกต่างจากภาษาที่ใช้งานได้จริง Rust ชอบโครงสร้างข้อมูลที่จำเป็นแบบดั้งเดิม ไม่มีรายการลิงก์ถาวร / ไม่เปลี่ยนแปลงในไลบรารีมาตรฐาน มีstd::collections::LinkedList
แต่มันก็เหมือนstd::list
ใน C ++ และท้อแท้ด้วยเหตุผลเดียวกับstd::list
(การใช้แคชไม่ดี)
อย่างไรก็ตามด้วยการอ้างอิงถึงชื่อของส่วนนี้ ("หน่วยความจำแบบแบ่งใช้ / ไม่แน่นอน"), Rust มีความแตกต่างอย่างหนึ่งกับ C ++: ขอแนะนำให้หน่วยความจำนั้นเป็น "ส่วนแบ่ง XOR ที่ไม่แน่นอน" เช่นหน่วยความจำนั้น เวลา. เปลี่ยนความทรงจำตามที่คุณต้องการ "ในความเป็นส่วนตัวของเธรดของคุณ" เพื่อพูด ตัดกับ C ++ โดยที่หน่วยความจำที่ไม่แน่นอนที่แบ่งใช้เป็นตัวเลือกเริ่มต้นและใช้กันอย่างแพร่หลาย
ในขณะที่กระบวนทัศน์ที่ใช้ร่วมกันของ xor-mutable นั้นมีความสำคัญอย่างมากต่อความแตกต่างด้านล่าง แต่มันก็เป็นกระบวนทัศน์การเขียนโปรแกรมที่แตกต่างกันมากซึ่งใช้เวลาสักครู่ในการทำความคุ้นเคย บางครั้งเราต้องยกเลิกกระบวนทัศน์นี้เช่นกับประเภทอะตอม ( AtomicUsize
เป็นสาระสำคัญของหน่วยความจำที่ไม่แน่นอนที่ใช้ร่วมกัน) โปรดทราบว่าการล็อกจะปฏิบัติตามกฎ shared-xor-mutable เนื่องจากจะออกกฎการอ่านและเขียนพร้อมกัน (ในขณะที่การเขียนเธรดหนึ่งเธรดไม่มีเธรดอื่นใดที่สามารถอ่านหรือเขียนได้)
ความแตกต่าง: การแข่งขันข้อมูลเป็นพฤติกรรมที่ไม่ได้กำหนด (UB)
หากคุณทริกเกอร์การแข่งขันของข้อมูลในรหัสสนิมมันจะจบลงเช่นเดียวกับใน C ++ การเดิมพันทั้งหมดปิดและผู้เรียบเรียงสามารถทำสิ่งที่มันพอใจ
อย่างไรก็ตามมันเป็นการรับประกันที่หนักหน่วงว่ารหัส Rust ที่ปลอดภัยไม่มีการแย่งข้อมูล (หรือ UB ใด ๆ สำหรับเรื่องนั้น) สิ่งนี้ขยายไปถึงภาษาแกนกลางและไปยังไลบรารีมาตรฐาน หากคุณสามารถเขียนโปรแกรม Rust ที่ไม่ได้ใช้unsafe
(รวมถึงในห้องสมุดบุคคลที่สาม แต่ไม่รวมไลบรารีมาตรฐาน) ซึ่งเรียกใช้ UB แสดงว่าเป็นข้อบกพร่องและจะได้รับการแก้ไข (สิ่งนี้เกิดขึ้นหลายครั้งแล้ว) หากหลักสูตรนี้แตกต่างอย่างสิ้นเชิงกับ C ++ ซึ่งเป็นเรื่องง่ายที่จะเขียนโปรแกรมด้วย UB
ความแตกต่าง: วินัยการล็อคที่เข้มงวด
ซึ่งแตกต่างจาก C ++ ล็อคใน Rust ( std::sync::Mutex
, std::sync::RwLock
ฯลฯ ) เป็นเจ้าของข้อมูลก็ปกป้อง แทนที่จะใช้การล็อกจากนั้นจัดการหน่วยความจำที่ใช้ร่วมกันบางอย่างที่เกี่ยวข้องกับการล็อกในเอกสารประกอบเท่านั้นข้อมูลที่แชร์จะไม่สามารถเข้าถึงได้ในขณะที่คุณไม่ได้ล็อคไว้ การ์ดรักษาความปลอดภัย RAII รักษาล็อคและให้การเข้าถึงข้อมูลที่ถูกล็อคพร้อมกัน (สามารถใช้ C ++ ได้ แต่สิ่งนี้ไม่ใช่std::
ล็อค) ระบบอายุการใช้งานช่วยให้มั่นใจได้ว่าคุณจะไม่สามารถเข้าถึงข้อมูลได้หลังจากที่คุณปลดล็อค (ดร็อปการ์ด RAII)
แน่นอนคุณสามารถมีล็อคที่ไม่มีข้อมูลที่เป็นประโยชน์ ( Mutex<()>
) และเพียงแบ่งปันหน่วยความจำบางส่วนโดยไม่ต้องเชื่อมโยงกับล็อคนั้นอย่างชัดเจน unsafe
แต่มีหน่วยความจำที่ใช้ร่วมกันอาจต้อง unsynchronized
ความแตกต่าง: การป้องกันการแบ่งปันโดยไม่ตั้งใจ
แม้ว่าคุณจะสามารถมีหน่วยความจำที่ใช้ร่วมกัน แต่คุณจะแบ่งปันเมื่อคุณขอมันอย่างชัดเจน ตัวอย่างเช่นเมื่อคุณใช้ข้อความผ่าน (เช่นช่องจากstd::sync
) เพื่อให้แน่ใจว่าระบบชีวิตของคุณไม่ให้การอ้างอิงใด ๆ กับข้อมูลหลังจากที่คุณส่งมาให้อีกด้วยค่ะ ในการแชร์ข้อมูลเบื้องหลังการล็อคคุณสร้างการล็อคอย่างชัดเจนและให้มันไปยังเธรดอื่น การใช้หน่วยความจำหมู่กับคุณดีต้องใช้unsafe
unsafe
สิ่งนี้เชื่อมโยงกับประเด็นต่อไป:
ความแตกต่าง: การติดตามความปลอดภัยของเธรด
ระบบชนิดของสนิมติดตามแนวคิดเรื่องความปลอดภัยของเธรด โดยเฉพาะSync
ประเภทลักษณะหมายถึงว่าสามารถใช้ร่วมกันโดยหลายกระทู้โดยไม่มีความเสี่ยงของการแข่งขันข้อมูลในขณะที่Send
เครื่องหมายที่สามารถเคลื่อนย้ายจากที่หนึ่งไปยังอีกด้าย สิ่งนี้ถูกบังคับใช้โดยคอมไพเลอร์ตลอดทั้งโปรแกรมและทำให้ผู้ออกแบบห้องสมุดกล้าทำการปรับแต่งที่อาจเป็นอันตรายอย่างน่าประหลาดใจหากไม่มีการตรวจสอบแบบคงที่เหล่านี้ ตัวอย่างเช่น C ++ std::shared_ptr
ซึ่งมักจะใช้การดำเนินการปรมาณูเพื่อจัดการจำนวนการอ้างอิงของมันเพื่อหลีกเลี่ยงการ UB ถ้าshared_ptr
เกิดขึ้นที่จะใช้โดยหลายกระทู้ สนิมมีRc
และArc
ซึ่งแตกต่างกันเฉพาะในที่Rc
ใช้การดำเนินการ refcount ไม่ใช่อะตอมและไม่ threadsafe (เช่นไม่ได้ใช้Sync
หรือSend
) ในขณะที่Arc
เป็นเหมือนshared_ptr
(และใช้ทั้งสองลักษณะ)
โปรดทราบว่าหากประเภทไม่ได้ใช้unsafe
เพื่อนำไปใช้ในการซิงโครไนซ์ด้วยตนเองการมีหรือไม่มีคุณลักษณะนั้นจะอนุมานได้อย่างถูกต้อง
ความแตกต่าง: กฎที่เข้มงวดมาก
ถ้าคอมไพเลอร์ไม่สามารถแน่ใจจริงๆว่ารหัสบางอย่างเป็นอิสระจากการแข่งขันข้อมูลและ UB อื่น ๆมันจะไม่รวบรวมระยะเวลา กฎดังกล่าวข้างต้นและเครื่องมืออื่น ๆ จะช่วยให้คุณออกไปได้ไกล แต่ไม่ช้าก็เร็วคุณจะต้องการทำสิ่งที่ถูกต้อง แต่ด้วยเหตุผลที่ละเอียดอ่อนซึ่งทำให้การแจ้งเตือนของคอมไพเลอร์หมดไป มันอาจเป็นโครงสร้างข้อมูลที่ปราศจากการล็อคที่ยุ่งยาก แต่ก็อาจเป็นสิ่งที่ทางโลกคิดว่า "ฉันเขียนไปยังตำแหน่งสุ่มในอาร์เรย์ที่ใช้ร่วมกัน
ณ จุดที่คุณสามารถกัด bullet และเพิ่มบิตของการประสานที่ไม่จำเป็นหรือคุณ reword รหัสดังกล่าวว่าคอมไพเลอร์สามารถดูความถูกต้อง (มักจะเป็นไปได้บางครั้งค่อนข้างยากที่เป็นไปไม่ได้ในบางครั้ง) หรือคุณวางลงในunsafe
รหัส แต่ถึงกระนั้นก็ยังมีค่าใช้จ่ายทางจิตใจเป็นพิเศษและ Rust ไม่ได้รับประกันความถูกต้องของunsafe
รหัส
ความแตกต่าง: เครื่องมือน้อยลง
เนื่องจากความแตกต่างดังกล่าวข้างต้นใน Rust มันหายากกว่ามากที่หนึ่งเขียนโค้ดที่อาจมีการแข่งขันข้อมูล (หรือใช้หลังจากฟรีหรือสองครั้งฟรีหรือ ... ) แม้ว่าสิ่งนี้จะเป็นสิ่งที่ดี แต่ก็มีผลข้างเคียงที่โชคร้ายที่ระบบนิเวศในการติดตามข้อผิดพลาดดังกล่าวยังด้อยพัฒนามากกว่าที่คาดไว้เนื่องจากเยาวชนและชุมชนมีขนาดเล็ก
ในขณะที่เครื่องมืออย่าง valgrind และ LLVM นั้นสามารถนำไปประยุกต์ใช้กับ Rust code ได้ แต่หลักการนี้ใช้ได้ผลจริง แต่แตกต่างกันไปในแต่ละเครื่องมือ - วันที่ทรัพยากรเกี่ยวกับวิธีการทำ) มันไม่ได้ช่วยอะไรมากนักที่ปัจจุบัน Rust ขาดสเปคที่แท้จริงและโดยเฉพาะอย่างยิ่งรูปแบบหน่วยความจำที่เป็นทางการ
กล่าวโดยย่อการเขียนunsafe
รหัส Rust อย่างถูกต้องนั้นยากกว่าการเขียนรหัส C ++ อย่างถูกต้องถึงแม้ว่าทั้งสองภาษานั้นจะมีความสามารถและความเสี่ยงในเชิงเปรียบเทียบกัน แน่นอนว่าสิ่งนี้จะต้องมีการถ่วงน้ำหนักกับความจริงที่ว่าโปรแกรม Rust ทั่วไปจะมีunsafe
โค้ดเพียงเล็กน้อยที่มีขนาดเล็กในขณะที่โปรแกรม C ++ นั้นก็คือ C ++ อย่างเต็มที่