ทำไมสนิมถึงมีString
และstr
? อะไรคือความแตกต่างระหว่างString
และstr
? เมื่อไหร่จะใช้String
แทนstr
และในทางกลับกัน? เป็นหนึ่งในพวกเขาได้รับการคัดค้าน?
ทำไมสนิมถึงมีString
และstr
? อะไรคือความแตกต่างระหว่างString
และstr
? เมื่อไหร่จะใช้String
แทนstr
และในทางกลับกัน? เป็นหนึ่งในพวกเขาได้รับการคัดค้าน?
คำตอบ:
String
เป็นสตริงสตริงฮีปแบบไดนามิกเช่นVec
: ใช้เมื่อคุณต้องการเป็นเจ้าของหรือแก้ไขข้อมูลสตริงของคุณ
str
เป็นไม่เปลี่ยนรูป1ลำดับของ UTF-8 ไบต์ของความยาวอยู่ที่ไหนสักแห่งในหน่วยความจำแบบไดนามิก เนื่องจากขนาดไม่เป็นที่รู้จักจึงสามารถจัดการกับด้านหลังตัวชี้ได้เท่านั้น ซึ่งหมายความว่าstr
โดยทั่วไป2จะปรากฏเป็น&str
: การอ้างอิงถึงข้อมูล UTF-8 บางอย่างซึ่งโดยปกติจะเรียกว่า "string slice" หรือเพียงแค่ "slice" ชิ้นงานเป็นเพียงการดูข้อมูลบางส่วนและข้อมูลนั้นสามารถอยู่ที่ใดก็ได้เช่น
"foo"
&'static str
ข้อมูลจะถูกเข้ารหัสลงในไฟล์เรียกทำงานและโหลดลงในหน่วยความจำเมื่อโปรแกรมทำงานString
: String
การ&str
ยกเลิกการกำหนดมุมมองของString
ข้อมูลในสแต็ก : เช่นต่อไปนี้สร้างอาร์เรย์ไบต์ที่จัดสรรสแต็กแล้วรับมุมมองของข้อมูลนั้นเป็น&str
:
use std::str;
let x: &[u8] = &[b'a', b'b', b'c'];
let stack_str: &str = str::from_utf8(x).unwrap();
โดยสรุปให้ใช้String
หากคุณต้องการข้อมูลสตริงที่เป็นเจ้าของ (เช่นการส่งสตริงไปยังเธรดอื่น ๆ หรือสร้างพวกมันตอนรันไทม์) และใช้&str
ถ้าคุณต้องการมุมมองของสตริงเท่านั้น
สิ่งนี้เหมือนกับความสัมพันธ์ระหว่างเวกเตอร์Vec<T>
กับชิ้น&[T]
หนึ่งและมีความคล้ายคลึงกับความสัมพันธ์ระหว่างค่าT
และการอ้างอิง&T
สำหรับชนิดทั่วไป
1 A str
มีความยาวคงที่ คุณไม่สามารถเขียนไบต์เกินกว่าสิ้นสุดหรือปล่อยไบต์ที่ไม่ถูกต้องต่อท้าย เนื่องจาก UTF-8 เป็นการเข้ารหัสความกว้างแปรผันการบังคับให้str
s ทั้งหมดนั้นไม่เปลี่ยนรูปในหลาย ๆ กรณี โดยทั่วไปการกลายพันธุ์จะต้องเขียนมากขึ้นหรือน้อยลงกว่าที่เคยมีมาก่อน (เช่นการแทนที่a
(1 ไบต์) ด้วยä
(2+ ไบต์) จะต้องทำให้มีที่ว่างมากขึ้นในstr
) มีวิธีการเฉพาะที่สามารถปรับเปลี่ยนให้&str
อยู่ในสถานที่ส่วนใหญ่เป็นผู้ที่จับเฉพาะอักขระ ASCII make_ascii_uppercase
เหมือน
2 ประเภทที่มีขนาดแบบไดนามิกช่วยให้สิ่งต่าง ๆ เช่นRc<str>
ลำดับของการอ้างอิงนับ UTF-8 นับตั้งแต่ Rust 1.2 Rust 1.21 ช่วยให้สามารถสร้างประเภทเหล่านี้ได้อย่างง่ายดาย
[u8; N]
มันไม่เป็นที่รู้จักแบบคงที่ซึ่งแตกต่างจากการพูด
Rc<str>
และArc<str>
ตอนนี้สามารถใช้งานได้ผ่านไลบรารีมาตรฐาน
ฉันมีพื้นหลัง C ++ และฉันคิดว่ามันมีประโยชน์มากที่จะคิดString
และเป็น&str
ศัพท์ C ++:
String
เป็นเหมือนstd::string
; มันเป็นเจ้าของหน่วยความจำและทำงานสกปรกในการจัดการหน่วยความจำ&str
เป็นเหมือนchar*
(แต่มีความซับซ้อนมากกว่า); std::string
มันชี้ให้เราสามารถเริ่มต้นของก้อนในลักษณะเดียวกับที่คุณจะได้รับตัวชี้ไปยังเนื้อหาของทั้งคู่จะหายไปหรือไม่? ฉันไม่คิดเช่นนั้น. พวกเขาให้บริการสองวัตถุประสงค์:
String
เก็บบัฟเฟอร์และใช้งานได้จริงมาก &str
มีน้ำหนักเบาและควรใช้เพื่อ "ค้นหา" ลงในสตริง คุณสามารถค้นหาแยกวิเคราะห์และแม้แต่แทนที่ชิ้นโดยไม่ต้องจัดสรรหน่วยความจำใหม่
&str
สามารถดูด้านในของ a String
ตามที่มันสามารถชี้ไปที่สตริงตัวอักษรบางอย่าง รหัสต่อไปนี้จำเป็นต้องคัดลอกสตริงตัวอักษรลงในString
หน่วยความจำที่มีการจัดการ:
let a: String = "hello rust".into();
รหัสต่อไปนี้ช่วยให้คุณใช้ตัวอักษรโดยไม่ต้องคัดลอก (อ่านเท่านั้น)
let a: &str = "hello rust";
str
ใช้เป็นเพียงเป็น&str
ชิ้นส่วนอ้างอิงถึงอาร์เรย์ UTF-8
String
เป็นสิ่งที่เคยเป็น~str
อาร์เรย์ไบต์ UTF-8 ที่เติบโตได้และเป็นเจ้าของ
~str
เป็นตอนนี้Box<str>
~str
เติบโตได้ในขณะที่Box<str>
ไม่สามารถเติบโตได้ (นั่น~str
และเติบโต~[T]
ได้อย่างน่าอัศจรรย์ไม่เหมือน~
-object อื่น ๆคืออะไรทำไมString
และVec<T>
ถูกนำมาใช้เพื่อให้กฎนั้นตรงไปตรงมาและสอดคล้องกัน)
พวกเขาแตกต่างอย่างสิ้นเชิงจริง ๆ ก่อนอื่น a str
คืออะไร แต่เป็นสิ่งที่ระดับประเภท; สามารถให้เหตุผลได้เฉพาะที่ระดับประเภทเท่านั้นเนื่องจากเป็นประเภทที่เรียกว่าแบบไดนามิก (DST) ขนาดที่str
ใช้ในการรวบรวมไม่สามารถทราบได้ในเวลารวบรวมและขึ้นอยู่กับข้อมูลรันไทม์ - ไม่สามารถเก็บไว้ในตัวแปรได้เนื่องจากคอมไพเลอร์จำเป็นต้องรู้ ณ เวลารวบรวมซึ่งขนาดของแต่ละตัวแปรคืออะไร A str
เป็นแนวคิดเพียงแถวของu8
ไบต์ด้วยการรับประกันว่ามันจะเป็นรูปแบบ UTF-8 ที่ถูกต้อง แถวใหญ่แค่ไหน ไม่มีใครรู้จนกระทั่งรันไทม์ดังนั้นจึงไม่สามารถเก็บไว้ในตัวแปร
สิ่งที่น่าสนใจก็คือว่า&str
หรือตัวชี้อื่น ๆ ไปstr
เหมือนBox<str>
จะมีอยู่ที่รันไทม์ นี่คือสิ่งที่เรียกว่า "ตัวชี้ไขมัน"; มันเป็นตัวชี้ที่มีข้อมูลเพิ่มเติม (ในกรณีนี้ขนาดของสิ่งที่ชี้ไป) ดังนั้นมันจึงใหญ่เป็นสองเท่า ในความเป็นจริงแล้ว a &str
ค่อนข้างใกล้กับ a String
(แต่ไม่ใช่ a &String
) A &str
คือคำสองคำ หนึ่งในตัวชี้ไปยังไบต์แรกของstr
และจำนวนที่อธิบายอีกว่าหลายไบต์ยาวstr
คือ
ตรงกันข้ามกับสิ่งที่ถูกกล่าวว่า a str
ไม่จำเป็นต้องไม่เปลี่ยนรูป หากคุณสามารถ&mut str
เป็นตัวชี้พิเศษให้กับstr
คุณสามารถกลายพันธุ์ได้และฟังก์ชั่นที่ปลอดภัยทั้งหมดที่กลายพันธุ์นั้นรับประกันได้ว่าข้อ จำกัด UTF-8 นั้นจะได้รับการรักษาเพราะถ้าหากมีการละเมิดเราจะมีพฤติกรรมที่ไม่ได้กำหนดไว้ จริงและไม่ได้ตรวจสอบ
ดังนั้นคือString
อะไร นั่นคือสามคำ สองเหมือนกัน&str
แต่จะเพิ่มคำที่สามซึ่งเป็นความจุของstr
บัฟเฟอร์บนฮีปเสมอบนฮีป (a str
ไม่จำเป็นต้องอยู่บนฮีป) จะจัดการก่อนที่จะเต็มและต้องจัดสรรใหม่ String
พื้นเป็นเจ้าของstr
ที่เขาเรียกว่า มันควบคุมและสามารถปรับขนาดและจัดสรรใหม่เมื่อเห็นว่าเหมาะสม ดังนั้นString
จะเป็นกล่าวใกล้ชิดกับกว่าไป&str
str
อีกสิ่งหนึ่งคือBox<str>
; สิ่งนี้ยังเป็นเจ้าของstr
และการแสดงแบบ runtime นั้นเหมือนกัน&str
แต่มันยังเป็นเจ้าของstr
ซึ่งแตกต่างจาก&str
แต่มันไม่สามารถปรับขนาดได้เพราะมันไม่ทราบความสามารถของมันดังนั้นโดยทั่วไป a Box<str>
สามารถถูกมองว่าเป็นความยาวคงString
ที่ แปลงเป็น a String
หากคุณต้องการปรับขนาดเสมอ)
มีความสัมพันธ์ที่คล้ายกันมากระหว่าง[T]
และVec<T>
ยกเว้นไม่มีข้อ จำกัด UTF-8 และสามารถเก็บประเภทใดก็ได้ที่มีขนาดไม่ไดนามิก
การใช้str
ในระดับประเภทส่วนใหญ่เพื่อสร้าง abstractions ทั่วไปด้วย&str
; มีอยู่ในระดับประเภทเพื่อให้สามารถเขียนคุณลักษณะได้อย่างสะดวก ในทางทฤษฎีแล้วstr
สิ่งที่เป็นประเภทไม่จำเป็นต้องมีอยู่จริงเท่านั้น&str
แต่นั่นหมายความว่าต้องมีการเขียนโค้ดพิเศษจำนวนมากซึ่งสามารถเป็นรหัสทั่วไปได้
&str
มีประโยชน์อย่างยิ่งที่จะสามารถมีหลาย ๆ substrings ของString
โดยไม่ต้องคัดลอก; อย่างที่String
เจ้าของบอกstr
ไว้บน heap มันจัดการและถ้าคุณสามารถสร้างซับสตริงของString
a ใหม่String
ได้มันจะต้องทำการคัดลอกเพราะทุกอย่างใน Rust สามารถมีเจ้าของเดียวเท่านั้นที่จะจัดการกับความปลอดภัยของหน่วยความจำได้ ตัวอย่างเช่นคุณสามารถแบ่งสตริง:
let string: String = "a string".to_string();
let substring1: &str = &string[1..3];
let substring2: &str = &string[2..4];
เรามีสตริงย่อยสองสตริงที่แตกต่างกันstr
ของสตริงเดียวกัน string
เป็นตัวที่เป็นเจ้าของstr
บัฟเฟอร์เต็มจริงบนฮีปและ&str
สตริงย่อยเป็นเพียงพอยน์เตอร์พอยน์เตอร์ไปยังบัฟเฟอร์นั้นบนฮีป
std::String
u8
เป็นเพียงเวกเตอร์ของ คุณสามารถค้นหาความหมายในรหัสที่มา มันเป็นกองที่จัดสรรและเติบโตได้
#[derive(PartialOrd, Eq, Ord)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct String {
vec: Vec<u8>,
}
str
เป็นชนิดดั้งเดิมที่เรียกว่าสตริงชิ้น ชิ้นสตริงมีขนาดคงที่ สตริงตัวอักษรเช่นlet test = "hello world"
มี&'static str
ประเภท test
เป็นการอ้างอิงถึงสตริงที่จัดสรรแบบสแตติกนี้
&str
ไม่สามารถแก้ไขได้เช่น
let mut word = "hello world";
word[0] = 's';
word.push('\n');
str
จะมีชิ้นที่ไม่แน่นอน&mut str
เช่น:
pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str)
let mut s = "Per Martin-Löf".to_string();
{
let (first, last) = s.split_at_mut(3);
first.make_ascii_uppercase();
assert_eq!("PER", first);
assert_eq!(" Martin-Löf", last);
}
assert_eq!("PER Martin-Löf", s);
แต่การเปลี่ยนแปลงเล็กน้อยเป็น UTF-8 สามารถเปลี่ยนความยาวไบต์ได้และชิ้นหนึ่งไม่สามารถจัดสรรการอ้างอิงได้
กล่าวง่ายๆString
คือประเภทข้อมูลถูกเก็บไว้ในกอง (เหมือนVec
) และคุณสามารถเข้าถึงตำแหน่งนั้นได้
&str
เป็นประเภทชิ้น นั่นหมายความว่ามันเป็นเพียงการอ้างอิงถึงปัจจุบันอยู่String
ที่ไหนสักแห่งในกอง
&str
ไม่ได้ทำการจัดสรรใด ๆ ที่รันไทม์ ดังนั้นสำหรับเหตุผลที่หน่วยความจำคุณสามารถใช้มากกว่า&str
String
แต่โปรดทราบว่าเมื่อใช้งาน&str
คุณอาจต้องจัดการกับอายุการใช้งานที่ชัดเจน
str
มันมีview
อยู่แล้วString
ในกอง
สำหรับคน C # และ Java:
String
===StringBuilder
&str
สตริง === (ไม่เปลี่ยนรูป) ของ Rustฉันชอบที่จะคิดว่า&str
เป็นมุมมองในสตริงเช่นสตริง interned ใน Java / C # ที่คุณไม่สามารถเปลี่ยนได้เพียงสร้างขึ้นใหม่
นี่คือคำอธิบายที่ง่ายและรวดเร็ว
String
- โครงสร้างข้อมูลที่จัดสรรได้และสามารถจัดสรรได้มาก &str
ก็สามารถที่จะบังคับให้เป็น
str
- คือ (ตอนนี้เป็นสนิมวิวัฒนาการ) สตริงที่มีความยาวคงที่ที่ไม่แน่นอนซึ่งอยู่บนฮีปหรือในไบนารี คุณเท่านั้นที่สามารถโต้ตอบกับเป็นชนิดที่ยืมมาผ่านมุมมองสตริงชิ้นเช่นstr
&str
ข้อควรพิจารณาการใช้งาน:
ต้องการString
ถ้าคุณต้องการเป็นเจ้าของหรือกลายพันธุ์สตริง - เช่นการส่งผ่านสตริงไปยังเธรดอื่นเป็นต้น
ชอบ&str
ถ้าคุณต้องการที่จะมีมุมมองแบบอ่านอย่างเดียวของสตริง
&str
ถูกสร้างขึ้นจากสององค์ประกอบตัวชี้ไปยังไบต์บางส่วนและความยาว."