[แปล] เข้าใจความเป็นเจ้าของในสนิม (2023)

ผู้แปลบทความนี้เป็นวิศวกรพัฒนาส่วนหน้าของ 360 Qiwu Troupe

ชื่อเดิม: ทำความเข้าใจความเป็นเจ้าของใน Rust

ผู้เขียนต้นฉบับ: Ukkai Ugochi

ลิงค์ต้นฉบับ: https://blog.logrocket.com/understanding-ownership-in-rust/

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

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

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

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

fn หลัก () {{ให้ x = 5 ;// x ถูกละทิ้งที่นี่เพราะมันอยู่นอกขอบเขต}}

สแต็คและฮีปคืออะไร?

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

หน่วยความจำถูกจัดเก็บไว้ในสแตกอย่างไร?สมมติว่ามีกองหนังสืออยู่บนโต๊ะ และหนังสือถูกจัดเรียงในลักษณะที่หนังสือเล่มสุดท้ายอยู่ด้านบนสุดของกองและหนังสือเล่มแรกอยู่ด้านล่างตามหลักการแล้ว เราไม่ต้องการให้หนังสือด้านล่างเลื่อนออกมาจากใต้กองหนังสือ การเลือกหนังสือมาอ่านจากด้านบนจะง่ายกว่า

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

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

ไม่มีองค์กรใดในฮีปเมื่อเทียบกับสแต็กการพุชข้อมูลเข้าและออกจากสแต็กเป็นเรื่องง่ายเพราะทุกอย่างถูกจัดระเบียบและเป็นไปตามคำสั่งเฉพาะระบบเข้าใจว่าเมื่อคุณพุชค่าไปยังสแต็ก ค่านั้นจะยังคงอยู่ที่ด้านบน และเมื่อคุณต้องการดึงค่าออกจากสแต็ก คุณกำลังดึงค่าสุดท้ายที่จัดเก็บไว้

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

การจัดสรรบนฮีปดูเหมือนการจัดทำดัชนีหนังสือ โดยที่ตัวชี้ไปยังค่าที่จัดเก็บไว้ในฮีปจะถูกจัดเก็บไว้ในสแต็กอย่างไรก็ตาม ตัวจัดสรรยังต้องค้นหาพื้นที่ว่างที่ใหญ่พอที่จะมีค่า

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

กฎความเป็นเจ้าของ

มีกฎพื้นฐานสามข้อในการเป็นเจ้าของที่ทำนายว่าหน่วยความจำถูกจัดเก็บไว้ในสแต็กและฮีปอย่างไร:

1. ทุกค่าของสนิมมีตัวแปรที่เรียกว่า "เจ้าของ":

ให้ x = 5 ; // x เป็นเจ้าของค่า "5"

2. แต่ละค่าสามารถมีเจ้าของได้ครั้งละหนึ่งคนเท่านั้น

3. เมื่อเจ้าของอยู่นอกขอบเขต ค่าจะถูกลบ:

fn หลัก () {{ // // ขอบเขตเริ่มต้นขึ้นให้ s = สตริง :: จาก ( "สวัสดี" ); // s เข้ามาในขอบเขต}// ค่าของ s ลดลง ณ จุดนี้ ซึ่งอยู่นอกขอบเขต}

ความเป็นเจ้าของทำงานอย่างไร

ในบทนำของเรา เราได้สร้างความจริงที่ว่าความเป็นเจ้าของไม่เหมือนกับระบบเก็บขยะภาษาการเขียนโปรแกรมส่วนใหญ่ใช้ตัวรวบรวมขยะหรือต้องการให้นักพัฒนาจัดสรรและเพิ่มหน่วยความจำเอง

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

fn หลัก () {{// a ไม่ถูกต้องที่นี่ให้ a = 5 ; // a ถูกต้องที่นี่// ทำสิ่งต่าง ๆ ด้วย a}println!("{}", a) // a ใช้ไม่ได้อีกต่อไป ณ จุดนี้ มันอยู่นอกขอบเขต}

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

สำหรับกรณีนี้ หน่วยความจำจะถูกจัดสรรบนฮีป และคุณต้องร้องขอหน่วยความจำก่อน ดังที่แสดงในตัวอย่างต่อไปนี้:

fn หลัก () {{ให้ mut s = สตริง :: จาก ( "สวัสดี"); // s ใช้ได้ตั้งแต่จุดนี้เป็นต้นไปpush_str ( ", โลก!" ); // push_str() ผนวกตัวอักษรเข้ากับสตริงprintln !( "{}" , s ); // สิ่งนี้จะพิมพ์ `hello, world!`}// s ใช้ไม่ได้อีกต่อไปที่นี่}

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

โคลนและคัดลอก

ในส่วนนี้ เราจะตรวจสอบว่าความเป็นเจ้าของส่งผลต่อฟีเจอร์บางอย่างใน Rust อย่างไร โดยเริ่มจากฟีเจอร์การโคลนและคัดลอก

สำหรับค่าที่มีขนาดที่รู้จัก (เช่น) จำนวนเต็ม การคัดลอกค่าไปยังค่าอื่นจะง่ายกว่าตัวอย่างเช่น:_

fn หลัก () {ให้ = "5" ;ให้ ข = ก ; // คัดลอกค่า a ไปยัง bprintln !( "{}" , ก ) // 5println !( "{}" , b ) // 5}

เนื่องจาก a ถูกเก็บไว้ในสแต็ก จึงง่ายกว่าที่จะคัดลอกค่าเพื่อสร้างสำเนา b อีกชุดหนึ่งนี่ไม่ใช่กรณีสำหรับค่าที่เก็บไว้ในฮีป:

fn หลัก () {ให้ a = สตริง :: จาก ( "สวัสดี" );ให้ ข = ก ; // คัดลอกค่า a ไปยัง bprintln !( "{}" , a ) // สิ่งนี้จะทำให้เกิดข้อผิดพลาดเนื่องจาก a ถูกย้ายหรือโอนความเป็นเจ้าของแล้วprintln !( "{}" , b ) // สวัสดี}

เมื่อคุณเรียกใช้คำสั่งนี้ คุณจะได้รับข้อผิดพลาด error[E0382]: ยืมค่าที่ย้าย: การย้าย "a"

ความเป็นเจ้าของและหน้าที่

การส่งค่าไปยังฟังก์ชันเป็นไปตามกฎความเป็นเจ้าของเดียวกัน หมายความว่าค่าเหล่านี้สามารถมีเจ้าของได้ครั้งละหนึ่งรายเท่านั้น และหน่วยความจำจะว่างทันทีเมื่อค่าเหล่านั้นอยู่นอกขอบเขตลองดูตัวอย่างนี้:

fn หลัก () {ให้ s1 = ให้ความเป็นเจ้าของ (); // giveOwnership ย้ายการกลับมาให้ s2 = สตริง::จาก ("สวัสดี"); // s2 เข้ามาอยู่ในขอบเขตให้ s3 = ใช้เวลาและให้กลับ (s2); // s2 ถูกย้ายไปที่takesAndGivesBack// ซึ่งจะย้ายค่าที่ส่งคืนไปยัง s3 ด้วย} // ในที่นี้ s3 อยู่นอกขอบเขตและหลุด s2 อยู่นอกขอบเขต แต่เป็น// ย้ายแล้ว ไม่มีอะไรเกิดขึ้น s1 อยู่นอกขอบเขตและตกหล่นfn giveOwnership() -> String { //giftOwnership จะย้ายมัน// ส่งค่ากลับเข้าไปในฟังก์ชัน//นั่นเรียก.ให้ someString = String::จาก ("สวัสดี"); // someString เข้ามาอยู่ในขอบเขตsomeString // someString ถูกส่งคืนและ//เดินออกไปตามเสียงเรียก// การทำงาน}// TakeAndGivesBack จะรับสตริงและส่งคืนหนึ่งสตริงfn takeAndGivesBack(aString: String) -> String { // aString เข้ามา// ขอบเขตaString // aString ถูกส่งคืนและย้ายออกไปยังฟังก์ชันการโทร}

ชิ้น

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

ลองดูตัวอย่างด้านล่างในตัวอย่างนี้ เราจะใช้ประเภทสไลซ์เพื่ออ้างถึงองค์ประกอบของค่าในลำดับที่ต่อเนื่องกัน:

fn หลัก () {ให้ s = String::จาก ("ไนจีเรีย");// &ประเภท strให้ a = &s[0..4];// ไม่โอนความเป็นเจ้าของ แต่อ้างอิง/ยืมตัวอักษรสี่ตัวแรกให้ b = &s[4..8]; // ไม่โอนความเป็นเจ้าของ แต่อ้างอิง/ยืมตัวอักษรสี่ตัวสุดท้ายprintln!("{}", ก); // พิมพ์ Nigeprintln!("{}", ข); //พิมพ์ไรอัน ให้ v = สิ่ง![1,2,3,4,5,6,7,8];// &[T] พิมพ์ให้ a = &v[0..4]; // ไม่โอนความเป็นเจ้าของ แต่อ้างอิง/ยืมองค์ประกอบสี่ตัวแรกให้ b = &v[4..8]; // ไม่โอนความเป็นเจ้าของ แต่อ้างอิง/ยืมองค์ประกอบสี่ตัวสุดท้ายprintln!("{:?}", ก); // พิมพ์ [1, 2, 3, 4]println!("{:?}", ข); // พิมพ์ [5, 6, 7, 8] }

สรุปแล้ว

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

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

-จบ-

เกี่ยวกับ Qi Wu Troupe

Qi Wu Troupe เป็นทีมส่วนหน้าที่ใหญ่ที่สุดของ 360 Group และมีส่วนร่วมในงานของสมาชิก W3C และ ECMA (TC39) ในนามของกลุ่มQi Wu Troupe ให้ความสำคัญอย่างยิ่งกับการฝึกอบรมผู้มีความสามารถพิเศษ และมีทิศทางการพัฒนาที่หลากหลาย เช่น วิศวกร ผู้บรรยาย นักแปล ผู้ติดต่อธุรกิจ และหัวหน้าทีมให้พนักงานเลือก และมีหลักสูตรการฝึกอบรมด้านเทคนิค วิชาชีพ ทั่วไป และความเป็นผู้นำที่เกี่ยวข้องQi Dance Troupe ยินดีต้อนรับผู้มีความสามารถที่โดดเด่นทุกประเภทเพื่อให้ความสนใจและเข้าร่วม Qi Dance Troupe ด้วยทัศนคติที่เปิดกว้างและแสวงหาความสามารถ

[แปล] เข้าใจความเป็นเจ้าของในสนิม (1)

References

Top Articles
Latest Posts
Article information

Author: Lidia Grady

Last Updated: 11/01/2024

Views: 5235

Rating: 4.4 / 5 (45 voted)

Reviews: 84% of readers found this page helpful

Author information

Name: Lidia Grady

Birthday: 1992-01-22

Address: Suite 493 356 Dale Fall, New Wanda, RI 52485

Phone: +29914464387516

Job: Customer Engineer

Hobby: Cryptography, Writing, Dowsing, Stand-up comedy, Calligraphy, Web surfing, Ghost hunting

Introduction: My name is Lidia Grady, I am a thankful, fine, glamorous, lucky, lively, pleasant, shiny person who loves writing and wants to share my knowledge and understanding with you.