Mengapa Rust memiliki String
dan str
? Apa perbedaan antara String
dan str
? Kapan seseorang menggunakan dan String
bukannya str
sebaliknya? Apakah salah satu dari mereka mulai ditinggalkan?
Mengapa Rust memiliki String
dan str
? Apa perbedaan antara String
dan str
? Kapan seseorang menggunakan dan String
bukannya str
sebaliknya? Apakah salah satu dari mereka mulai ditinggalkan?
Jawaban:
String
adalah tipe string tumpukan dinamis, seperti Vec
: gunakan ketika Anda perlu memiliki atau memodifikasi data string Anda.
str
adalah 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 str
paling 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
"foo"
adalah a &'static str
. Data di-hardcode ke dalam executable dan dimuat ke dalam memori ketika program berjalan.String
: String
dereferences ke &str
tampilan dari String
data 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 String
jika Anda membutuhkan data string yang dimiliki (seperti meneruskan string ke utas lain, atau membangunnya saat runtime), dan gunakan &str
jika Anda hanya memerlukan tampilan string.
Ini identik dengan hubungan antara vektor Vec<T>
dan irisan &[T]
, dan mirip dengan hubungan antara nilai- T
dan referensi- &T
untuk tipe umum.
1 A str
adalah 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 str
s 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 &str
di 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.
[u8; N]
,.
Rc<str>
dan Arc<str>
sekarang dapat digunakan melalui perpustakaan standar.
Saya memiliki latar belakang C ++ dan saya merasa sangat berguna untuk memikirkan String
dan &str
dalam istilah C ++:
String
seperti std::string
; ia memiliki memori dan melakukan pekerjaan kotor mengelola memori.&str
seperti 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:
String
menjaga buffer dan sangat praktis untuk digunakan. &str
ringan dan harus digunakan untuk "melihat" string. Anda dapat mencari, membagi, mem-parsing, dan bahkan mengganti potongan tanpa perlu mengalokasikan memori baru.
&str
dapat melihat ke dalam String
karena dapat menunjuk ke beberapa string literal. Kode berikut perlu menyalin string literal ke dalam String
memori 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";
str
, hanya digunakan sebagai &str
, adalah slice string, referensi ke array byte UTF-8.
String
adalah apa yang dulunya adalah ~str
, sebuah array byte UTF-8 yang dapat ditanam, dimiliki.
~str
adalah sekarangBox<str>
~str
sudah bisa ditanami sementara Box<str>
tidak bisa ditanami. (Itu ~str
dan ~[T]
dapat ditumbuhkan secara ajaib, tidak seperti objek lainnya ~
, persis mengapa String
dan Vec<T>
diperkenalkan, sehingga aturannya mudah dan konsisten.)
Mereka sebenarnya sangat berbeda. Pertama, a str
tidak lain adalah level level; itu hanya dapat dipertimbangkan pada tingkat tipe karena itu disebut tipe ukuran dinamis (DST). Ukuran str
memakan 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 str
secara konseptual hanyalah deretan u8
byte 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 &str
atau pointer lain untuk str
seperti 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 &str
cukup dekat dengan String
(tetapi tidak ke a &String
). A &str
adalah dua kata; satu pointer ke byte pertama dari str
dan nomor lain yang menggambarkan berapa byte panjangnya str
.
Bertentangan dengan apa yang dikatakan, a str
tidak perlu abadi. Jika Anda bisa mendapatkan &mut str
sebagai 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 &str
tetapi menambahkan kata ketiga yang merupakan kapasitas str
buffer di heap, selalu di heap (a str
tidak harus di heap) itu dikelola sebelum diisi dan harus mengalokasikan kembali. yang String
pada dasarnya memiliki sebuah str
seperti yang mereka katakan; itu mengontrolnya dan dapat mengubah ukurannya dan mengalokasikannya kembali jika dianggap cocok. Jadi String
seperti yang dikatakan lebih dekat ke &str
daripada ke str
.
Hal lain adalah Box<str>
; ini juga memiliki str
dan representasi runtime-nya sama dengan &str
tetapi ia juga memiliki yang str
tidak seperti &str
itu tetapi tidak dapat mengubah ukurannya karena tidak mengetahui kapasitasnya sehingga pada dasarnya a Box<str>
dapat dilihat sebagai panjang tetap String
yang tidak dapat diubah ukurannya (Anda dapat selalu ubah menjadi String
jika 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 str
pada level tipe sebagian besar untuk membuat abstraksi generik dengan &str
; itu ada pada tingkat tipe untuk dapat dengan mudah menulis ciri. Secara teori str
sebagai tipe hal tidak perlu ada dan hanya &str
tetapi itu berarti banyak kode tambahan harus ditulis yang sekarang bisa menjadi generik.
&str
sangat berguna untuk dapat memiliki beberapa substring yang berbeda String
tanpa harus menyalin; sebagai kata seorang String
memiliki yang str
pada tumpukan itu berhasil dan jika Anda hanya bisa membuat substring dari String
dengan baru String
itu 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 str
dari string yang sama. string
adalah salah satu yang memiliki str
buffer penuh aktual pada heap dan &str
substring hanya pointer gemuk ke buffer di heap.
std::String
hanyalah 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>,
}
str
adalah tipe primitif, juga disebut string slice . Irisan string memiliki ukuran tetap. String literal seperti let test = "hello world"
memiliki &'static str
tipe. test
adalah referensi untuk string yang dialokasikan secara statis ini.
&str
tidak dapat dimodifikasi, misalnya,
let mut word = "hello world";
word[0] = 's';
word.push('\n');
str
memang 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.
Dengan kata mudah, String
apakah tipe data disimpan di heap (sama seperti Vec
), dan Anda memiliki akses ke lokasi itu.
&str
adalah tipe slice. Itu berarti itu hanya referensi ke suatu tempat yang sudah ada String
di tumpukan.
&str
tidak melakukan alokasi apa pun pada saat runtime. Jadi, untuk alasan memori, Anda dapat menggunakan &str
lebih dari itu String
. Namun, perlu diingat bahwa ketika menggunakan &str
Anda mungkin harus berurusan dengan kehidupan eksplisit.
str
adalah view
dari yang sudah ada String
di tumpukan.
Untuk orang-orang C # dan Java:
String
===StringBuilder
&str
String === (tidak berubah) dari RustSaya suka menganggap &str
sebagai tampilan pada string, seperti string yang diinternir di Java / C # di mana Anda tidak dapat mengubahnya, hanya membuat yang baru.
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 str
sebagai tipe pinjaman melalui tampilan slice string, seperti &str
.
Pertimbangan penggunaan:
Lebih suka String
jika Anda ingin memiliki atau bermutasi string - seperti meneruskan string ke utas lainnya, dll.
Lebih suka &str
jika Anda ingin memiliki tampilan string hanya-baca.
&str
terdiri dari dua komponen: pointer ke beberapa byte, dan panjang a."