Apa perbedaan antara `String` dan` str` di Rust?

421

Mengapa Rust memiliki Stringdan str? Apa perbedaan antara Stringdan str? Kapan seseorang menggunakan dan Stringbukannya strsebaliknya? Apakah salah satu dari mereka mulai ditinggalkan?

Daniel Fath
sumber

Jawaban:

491

Stringadalah tipe string tumpukan dinamis, seperti Vec: gunakan ketika Anda perlu memiliki atau memodifikasi data string Anda.

stradalah 1 urutan UTF-8 byte panjang dinamis yang tidak dapat diubah di suatu tempat di memori. Karena ukurannya tidak diketahui, orang hanya bisa menanganinya di belakang pointer. Ini berarti bahwa strpaling umum 2 muncul sebagai &str: referensi ke beberapa data UTF-8, biasanya disebut "string slice" atau hanya "slice". Sepotong hanyalah pandangan ke beberapa data, dan data itu bisa di mana saja, misalnya

  • Dalam penyimpanan statis : string literal "foo"adalah a &'static str. Data di-hardcode ke dalam executable dan dimuat ke dalam memori ketika program berjalan.
  • Di dalam tumpukan dialokasikanString : Stringdereferences ke &strtampilan dari Stringdata yang 's.
  • Pada stack : misalnya yang berikut ini membuat array byte yang dialokasikan stack, dan kemudian mendapatkan tampilan data itu sebagai&str :

    use std::str;
    
    let x: &[u8] = &[b'a', b'b', b'c'];
    let stack_str: &str = str::from_utf8(x).unwrap();
    

Singkatnya, gunakan Stringjika Anda membutuhkan data string yang dimiliki (seperti meneruskan string ke utas lain, atau membangunnya saat runtime), dan gunakan &strjika Anda hanya memerlukan tampilan string.

Ini identik dengan hubungan antara vektor Vec<T>dan irisan &[T], dan mirip dengan hubungan antara nilai- Tdan referensi- &Tuntuk tipe umum.


1 A stradalah panjang tetap; Anda tidak dapat menulis byte di luar akhirnya, atau meninggalkan byte yang tidak valid. Karena UTF-8 adalah pengodean lebar variabel, ini secara efektif memaksa semua strs tidak berubah dalam banyak kasus. Secara umum, mutasi memerlukan penulisan byte lebih banyak atau lebih sedikit daripada sebelumnya (misalnya mengganti a(1 byte) dengan ä(2+ byte) akan membutuhkan ruang lebih banyak di dalam str). Ada metode khusus yang dapat memodifikasi &strdi tempat, kebanyakan yang hanya menangani karakter ASCII, seperti make_ascii_uppercase.

2 Jenis berukuran dinamis memungkinkan hal-hal seperti Rc<str>untuk urutan referensi dihitung UTF-8 byte sejak Rust 1.2. Karat 1.21 memungkinkan dengan mudah membuat jenis ini.

huon
sumber
10
"urutan byte UTF-8 (dengan panjang tidak diketahui )" - apakah ini kedaluwarsa? The docs mengatakan "A &strterdiri dari dua komponen: pointer ke beberapa byte, dan panjang a."
mrec
11
Ini tidak ketinggalan zaman (bahwa representasi telah cukup stabil), hanya sedikit tidak tepat: tidak diketahui secara statis, tidak seperti, katakanlah [u8; N],.
huon
2
@mrec itu tidak diketahui pada waktu kompilasi, asumsi tentang ukuran itu tidak dapat dibuat, misalnya, ketika membuat bingkai stack. Jadi mengapa sering diperlakukan sebagai referensi, yang referensi adalah ukuran yang diketahui pada waktu kompilasi, yang merupakan ukuran pointer.
Sekhat
1
Perbarui: Rc<str>dan Arc<str>sekarang dapat digunakan melalui perpustakaan standar.
Centril
1
@ cjohansson Objek yang dialokasikan secara statis biasanya tidak disimpan di heap, atau di stack, tetapi di wilayah memori mereka sendiri.
Brennan Vincent
97

Saya memiliki latar belakang C ++ dan saya merasa sangat berguna untuk memikirkan Stringdan &strdalam istilah C ++:

  • Karat Stringseperti std::string; ia memiliki memori dan melakukan pekerjaan kotor mengelola memori.
  • Karat &strseperti char*(tetapi sedikit lebih canggih); itu mengarahkan kita ke awal chunk dengan cara yang sama Anda bisa mendapatkan pointer ke isi std::string.

Apakah salah satu dari mereka akan menghilang? Saya kira tidak. Mereka melayani dua tujuan:

Stringmenjaga buffer dan sangat praktis untuk digunakan. &strringan dan harus digunakan untuk "melihat" string. Anda dapat mencari, membagi, mem-parsing, dan bahkan mengganti potongan tanpa perlu mengalokasikan memori baru.

&strdapat melihat ke dalam Stringkarena dapat menunjuk ke beberapa string literal. Kode berikut perlu menyalin string literal ke dalam Stringmemori yang dikelola:

let a: String = "hello rust".into();

Kode berikut memungkinkan Anda menggunakan literal itu sendiri tanpa salinan (hanya baca saja)

let a: &str = "hello rust";
Luis Ayuso
sumber
13
seperti string_view?
Abhinav Gauniyal
2
Ya seperti string_view tetapi intrinsik ke bahasa dan meminjam dengan benar diperiksa.
locka
41

str, hanya digunakan sebagai &str, adalah slice string, referensi ke array byte UTF-8.

Stringadalah apa yang dulunya adalah ~str, sebuah array byte UTF-8 yang dapat ditanam, dimiliki.

Chris Morgan
sumber
Secara teknis, apa yang dulu ~stradalah sekarangBox<str>
jv110
3
@ jv110: tidak, karena ~strsudah bisa ditanami sementara Box<str>tidak bisa ditanami. (Itu ~strdan ~[T]dapat ditumbuhkan secara ajaib, tidak seperti objek lainnya ~, persis mengapa Stringdan Vec<T>diperkenalkan, sehingga aturannya mudah dan konsisten.)
Chris Morgan
18

Mereka sebenarnya sangat berbeda. Pertama, a strtidak lain adalah level level; itu hanya dapat dipertimbangkan pada tingkat tipe karena itu disebut tipe ukuran dinamis (DST). Ukuran strmemakan waktu tidak dapat diketahui pada waktu kompilasi dan tergantung pada informasi runtime - itu tidak dapat disimpan dalam variabel karena kompiler perlu tahu pada waktu kompilasi berapa ukuran masing-masing variabel. A strsecara konseptual hanyalah deretan u8byte dengan jaminan bahwa itu membentuk UTF-8 yang valid. Berapa besar barisnya? Tidak ada yang tahu sampai runtime karena itu tidak dapat disimpan dalam variabel.

Hal yang menarik adalah bahwa &stratau pointer lain untuk strseperti Box<str> tidak eksis pada saat runtime. Ini disebut "penunjuk gemuk"; itu adalah penunjuk dengan informasi tambahan (dalam hal ini ukuran benda yang ditunjuknya) sehingga dua kali lebih besar. Bahkan, a &strcukup dekat dengan String(tetapi tidak ke a &String). A &stradalah dua kata; satu pointer ke byte pertama dari strdan nomor lain yang menggambarkan berapa byte panjangnya str.

Bertentangan dengan apa yang dikatakan, a strtidak perlu abadi. Jika Anda bisa mendapatkan &mut strsebagai penunjuk eksklusif ke str, Anda dapat bermutasi dan semua fungsi aman yang bermutasi menjamin bahwa batasan UTF-8 ditegakkan karena jika itu dilanggar maka kami memiliki perilaku yang tidak ditentukan karena perpustakaan menganggap batasan ini adalah benar dan tidak memeriksa untuk itu.

Jadi, apa itu String? Itu tiga kata; keduanya sama seperti untuk &strtetapi menambahkan kata ketiga yang merupakan kapasitas strbuffer di heap, selalu di heap (a strtidak harus di heap) itu dikelola sebelum diisi dan harus mengalokasikan kembali. yang Stringpada dasarnya memiliki sebuah strseperti yang mereka katakan; itu mengontrolnya dan dapat mengubah ukurannya dan mengalokasikannya kembali jika dianggap cocok. Jadi Stringseperti yang dikatakan lebih dekat ke &strdaripada ke str.

Hal lain adalah Box<str>; ini juga memiliki strdan representasi runtime-nya sama dengan &strtetapi ia juga memiliki yang strtidak seperti &stritu tetapi tidak dapat mengubah ukurannya karena tidak mengetahui kapasitasnya sehingga pada dasarnya a Box<str>dapat dilihat sebagai panjang tetap Stringyang tidak dapat diubah ukurannya (Anda dapat selalu ubah menjadi Stringjika Anda ingin mengubah ukurannya).

Hubungan yang sangat mirip ada antara [T]dan Vec<T>kecuali tidak ada batasan UTF-8 dan dapat menampung semua jenis yang ukurannya tidak dinamis.

Penggunaan strpada level tipe sebagian besar untuk membuat abstraksi generik dengan &str; itu ada pada tingkat tipe untuk dapat dengan mudah menulis ciri. Secara teori strsebagai tipe hal tidak perlu ada dan hanya &strtetapi itu berarti banyak kode tambahan harus ditulis yang sekarang bisa menjadi generik.

&strsangat berguna untuk dapat memiliki beberapa substring yang berbeda Stringtanpa harus menyalin; sebagai kata seorang String memiliki yang strpada tumpukan itu berhasil dan jika Anda hanya bisa membuat substring dari Stringdengan baru Stringitu harus disalin karena segala sesuatu di Rust hanya dapat memiliki satu pemilik tunggal untuk menangani keamanan memori. Jadi misalnya Anda dapat mengiris string:

let string: String   = "a string".to_string();
let substring1: &str = &string[1..3];
let substring2: &str = &string[2..4];

Kami memiliki dua substring berbeda strdari string yang sama. stringadalah salah satu yang memiliki strbuffer penuh aktual pada heap dan &strsubstring hanya pointer gemuk ke buffer di heap.

Zorf
sumber
4

std::Stringhanyalah sebuah vektor dari u8. Anda dapat menemukan definisinya dalam kode sumber . Ini tumpukan dialokasikan dan ditumbuhkan.

#[derive(PartialOrd, Eq, Ord)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct String {
    vec: Vec<u8>,
}

stradalah tipe primitif, juga disebut string slice . Irisan string memiliki ukuran tetap. String literal seperti let test = "hello world"memiliki &'static strtipe. testadalah referensi untuk string yang dialokasikan secara statis ini. &strtidak dapat dimodifikasi, misalnya,

let mut word = "hello world";
word[0] = 's';
word.push('\n');

strmemang memiliki irisan yang bisa berubah &mut str, misalnya: 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);

Tetapi perubahan kecil ke UTF-8 dapat mengubah panjang byte-nya, dan irisan tidak dapat realokasi referensi.

Aperion
sumber
0

Dengan kata mudah, Stringapakah tipe data disimpan di heap (sama seperti Vec), dan Anda memiliki akses ke lokasi itu.

&stradalah tipe slice. Itu berarti itu hanya referensi ke suatu tempat yang sudah ada Stringdi tumpukan.

&strtidak melakukan alokasi apa pun pada saat runtime. Jadi, untuk alasan memori, Anda dapat menggunakan &strlebih dari itu String. Namun, perlu diingat bahwa ketika menggunakan &strAnda mungkin harus berurusan dengan kehidupan eksplisit.

00imvj00
sumber
1
suatu tempat di tumpukan - itu tidak sepenuhnya akurat.
Shepmaster
Yang saya maksudkan adalah bahwa stradalah viewdari yang sudah ada Stringdi tumpukan.
00imvj00
1
Saya mengerti itu yang Anda maksud, dan saya katakan itu tidak sepenuhnya akurat. "Heap" bukan bagian yang diperlukan dari pernyataan.
Shepmaster
-1

Untuk orang-orang C # dan Java:

  • Karat ' String===StringBuilder
  • &str String === (tidak berubah) dari Rust

Saya suka menganggap &strsebagai tampilan pada string, seperti string yang diinternir di Java / C # di mana Anda tidak dapat mengubahnya, hanya membuat yang baru.

Tupai
sumber
1
Perbedaan terbesar antara string Java / C # dan string Rust adalah bahwa Rust menjamin string menjadi unicode yang benar, karena mendapatkan karakter ketiga dalam string memerlukan pemikiran lebih dari sekadar "abc" [2]. (Mengingat kita hidup di dunia multi-bahasa, ini adalah hal yang baik.)
Squirrel
Ini salah . Topik mutabilitas sudah dibahas dalam jawaban terpilih; silakan baca untuk mempelajari lebih lanjut.
Shepmaster
-5

Berikut ini penjelasan yang cepat dan mudah.

String- Struktur data yang dialokasikan dan dapat ditimbun milik sendiri. Itu bisa dipaksa untuk &str.

str- adalah (sekarang, ketika Rust berevolusi) string yang dapat berubah, tetap-panjang yang hidup di heap atau dalam biner. Anda hanya dapat berinteraksi dengan strsebagai tipe pinjaman melalui tampilan slice string, seperti &str.

Pertimbangan penggunaan:

Lebih suka Stringjika Anda ingin memiliki atau bermutasi string - seperti meneruskan string ke utas lainnya, dll.

Lebih suka &strjika Anda ingin memiliki tampilan string hanya-baca.

Pengembang
sumber
Ini salah . Topik mutabilitas sudah dibahas dalam jawaban terpilih; silakan baca untuk mempelajari lebih lanjut.
Shepmaster