เป็นไปได้ที่จะบรรลุถึงรูปแบบการเป็นเจ้าของของ Rust ด้วยเสื้อคลุม C ++ ทั่วไปหรือไม่


15

ดูบทความนี้เกี่ยวกับความปลอดภัยของการเกิดพร้อมกันของ Rust:

http://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html

ฉันสงสัยว่าความคิดเหล่านี้สามารถทำได้ใน C ++ 11 (หรือใหม่กว่า) โดยเฉพาะอย่างยิ่งฉันสามารถสร้างคลาสของเจ้าของที่โอนความเป็นเจ้าของไปยังวิธีการใด ๆ ที่มันอาจจะผ่าน? ดูเหมือนว่า C ++ มีหลายวิธีในการส่งผ่านตัวแปรที่เป็นไปไม่ได้ แต่บางทีฉันอาจวางข้อ จำกัด บางอย่างในคลาสหรือเทมเพลตเพื่อให้แน่ใจว่าโค้ดเทมเพลตบางตัวได้รับการประมวลผลด้วยวิธีการส่งผ่านทุกครั้งหรือไม่


บางคำพูดจากลิงค์จะปรับปรุงคำถามนี้
มาร์ตินบา

2
@delnan (ปลอดภัย) สนิมรับประกันได้ว่าคุณไม่เคยมีการอ้างอิงที่ไม่แน่นอนมากกว่าหนึ่งครั้งกับบางสิ่งและคุณไม่เคยมีการอ้างอิงที่ไม่แน่นอนกับสิ่งที่คุณกำลังมีการอ้างอิงแบบอ่านอย่างเดียว นอกจากนี้ยังมีข้อ จำกัด บางประการเกี่ยวกับการถ่ายโอนข้อมูลระหว่างเธรด การรวมกันเหล่านี้จะป้องกันคลาสที่สำคัญของเธรดบั๊กที่เกี่ยวข้องและให้เหตุผลเกี่ยวกับสถานะของวัตถุได้ง่ายขึ้นแม้ในรหัสเธรดเดี่ยว
CodesInChaos

3
คุณไม่คิดว่าคุณสามารถแสดงการยืมในลักษณะที่คอมไพเลอร์ C ++ สามารถตรวจสอบได้ดังนั้นคุณต้องหันไปใช้การบังคับใช้แบบรันไทม์พร้อมกับประสิทธิภาพที่เกี่ยวข้อง
CodesInChaos

1
ขอบเขตการเป็นเจ้าของไม่ได้ถูกใช้งานโดยตัวชี้สมาร์ทใน C ++ 11 หรือไม่
Akshat Mahajan

1
@JerryJeremiah Rust มีหลากหลายประเภทอ้างอิง สิ่งที่เป็นพื้นฐาน&ไม่ต้องการการส่งเสริมใด ๆ ที่จะใช้ หากคุณพยายามที่จะได้รับ&mutในขณะที่คุณยังคงมีการอ้างอิงอื่น (ไม่แน่นอนหรือไม่) เพื่อรายการเดียวกันคุณจะไม่สามารถรวบรวม RefCell<T>ย้ายเช็คไปเรียกใช้เวลาดังนั้นคุณจะได้รับความหวาดกลัวถ้าคุณพยายามที่จะ.borrow_mut()มีอะไรบางอย่างที่มีอยู่แล้วมีการใช้งานหรือ.borrow() .borrow_mut()สนิมยังมีRc<T>(ตัวชี้การเป็นเจ้าของร่วม) และพี่น้องWeak<T>แต่สิ่งเหล่านี้เกี่ยวกับความเป็นเจ้าของไม่ใช่ความผันแปร ติดRefCell<T>อยู่ข้างในพวกเขาเพื่อความไม่แน่นอน
8bittree

คำตอบ:


8

C ++ มีสามวิธีในการส่งผ่านพารามิเตอร์ไปยังฟังก์ชัน: ตามค่าโดยอ้างอิง lvalue และโดยอ้างอิง rvalue ในบรรดาสิ่งเหล่านี้การส่งผ่านตามตัวอักษรจะสร้างความเป็นเจ้าของในแง่ที่ว่าฟังก์ชั่นที่เรียกได้รับการคัดลอกของตัวเองและการส่งผ่านโดยการอ้างอิง rvalue บ่งชี้ว่าค่าอาจถูกบริโภคนั่นคือจะไม่ถูกใช้ ผ่านการอ้างอิง lvalue หมายความว่าวัตถุนั้นยืมมาจากผู้โทรชั่วคราว

อย่างไรก็ตามสิ่งเหล่านี้มีแนวโน้มที่จะ "โดยการประชุม" และไม่สามารถตรวจสอบได้โดยคอมไพเลอร์ และคุณสามารถเปลี่ยนการอ้างอิง lvalue เป็นการอ้างอิง rvalue ได้โดยstd::move()ไม่ตั้งใจ เป็นรูปธรรมมีสามปัญหา:

  • การอ้างอิงสามารถอยู่ได้นานกว่าวัตถุที่อ้างอิง ระบบอายุการใช้งานของสนิมป้องกันสิ่งนี้

  • สามารถมีการอ้างอิงที่ไม่แน่นอน / ไม่ใช่ const มากกว่าหนึ่งรายการที่ใช้งานได้ตลอดเวลา ตัวตรวจสอบการยืมของสนิมป้องกันสิ่งนี้

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

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

std::weak_ptrชี้สมาร์ทไม่ความหมายเป็นเจ้าของเข้ารหัสคล้ายกับการอ้างอิงธรรมดา แต่ต้องว่าวัตถุที่อ้างถึงมีการจัดการผ่านทางshared_ptrเช่นการอ้างอิงนับ นี่ไม่ใช่สิ่งที่เป็นนามธรรมโดยไม่มีค่าใช้จ่าย

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

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

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

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


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

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

ฉันเห็นว่าขอบคุณ
Elliot Gorokhovsky
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.