鍍金池/ 教程/ Java/ 字符串
哲學(xué)家就餐問題
鏈接進(jìn)階
名詞中英文對照
測試
引用和借用
泛型
方法語法
函數(shù)
不安全代碼
并發(fā)
裝箱語法和模式
注釋
棧和堆
運(yùn)算符與重載
語法索引
文檔
固有功能
所有權(quán)
循環(huán)
通用函數(shù)調(diào)用語法
不定長類型
<code>const</code> 和 <code>static</code>
迭代器
其他語言中的 Rust
枚舉
詞匯表
If語句
猜猜看
錯(cuò)誤處理
生命周期
編譯器插件
發(fā)布途徑
閉包
trait 對象
不使用標(biāo)準(zhǔn)庫
關(guān)聯(lián)常量
外部函數(shù)接口(FFI)
類型轉(zhuǎn)換
原生類型
匹配
參考文獻(xiàn)
Rust 編程語言
內(nèi)聯(lián)匯編
條件編譯
選擇你的保證
學(xué)習(xí) Rust
`type`別名
自定義內(nèi)存分配器
屬性
if let
高效 Rust
可變性
語法和語義
模式
基準(zhǔn)測試
結(jié)構(gòu)體
變量綁定
語言項(xiàng)
切片模式
<code>Deref</code> 強(qiáng)制多態(tài)
關(guān)聯(lián)類型
裸指針
<code>Borrow</code> 和 <code>AsRef</code>
準(zhǔn)備
Rust 開發(fā)版
字符串

字符串

strings.md
commit 6ba952020fbc91bad64be1ea0650bfba52e6aab4

對于每一個(gè)程序,字符串都是需要掌握的重要內(nèi)容。由于Rust主要著眼于系統(tǒng)編程,所以它的字符串處理系統(tǒng)與其它語言有些許區(qū)別。每當(dāng)你碰到一個(gè)可變大小的數(shù)據(jù)結(jié)構(gòu)時(shí),情況都會變得很微妙,而字符串正是可變大小的數(shù)據(jù)結(jié)構(gòu)。這也就是說,Rust的字符串與一些像C這樣的系統(tǒng)編程語言也不相同。

讓我們進(jìn)一步了解一下。一個(gè)字符串是一串UTF-8字節(jié)編碼的Unicode量級值的序列。所有的字符串都確保是有效編碼的UTF-8序列。另外,字符串并不以null結(jié)尾并且可以包含null字節(jié)。

Rust有兩種主要的字符串類型:&strString。讓我們先看看&str。這叫做字符串片段string slices)。字符串常量是&'static str類型的:

let greeting = "Hello there."; // greeting: &'static str

"Hello there."是一個(gè)字符串常量而它的類型是&'static str。字符串常量是靜態(tài)分配的字符串切片,也就是說它儲存在我們編譯好的程序中,并且整個(gè)程序的運(yùn)行過程中一直存在。這個(gè)greeting綁定了一個(gè)靜態(tài)分配的字符串的引用。任何接受一個(gè)字符串切片的函數(shù)也接受一個(gè)字符串常量。

字符串常量可以跨多行。有兩種形式。第一種會包含新行符和之前的空格:

let s = "foo
    bar";

assert_eq!("foo\n        bar", s);

第二種,帶有\,會去掉空格和新行符:

let s = "foo\
    bar"; 

assert_eq!("foobar", s);

Rust 當(dāng)然不僅僅只有&str。一個(gè)String,是一個(gè)在堆上分配的字符串。這個(gè)字符串可以增長,并且也保證是UTF-8編碼的。String通常通過一個(gè)字符串片段調(diào)用to_string方法轉(zhuǎn)換而來。

let mut s = "Hello".to_string(); // mut s: String
println!("{}", s);

s.push_str(", world.");
println!("{}", s);

String可以通過一個(gè)&強(qiáng)制轉(zhuǎn)換為&str

fn takes_slice(slice: &str) {
    println!("Got: {}", slice);
}

fn main() {
    let s = "Hello".to_string();
    takes_slice(&s);
}

這種強(qiáng)制轉(zhuǎn)換并不發(fā)生在接受&str的trait而不是&str本身作為參數(shù)的函數(shù)上。例如,TcpStream::connect,有一個(gè)ToSocketAddrs類型的參數(shù)。&str可以不用轉(zhuǎn)換不過String必須使用&*顯式轉(zhuǎn)換。

use std::net::TcpStream;

TcpStream::connect("192.168.0.1:3000"); // &str parameter

let addr_string = "192.168.0.1:3000".to_string();
TcpStream::connect(&*addr_string); // convert addr_string to &str

String轉(zhuǎn)換為&str的代價(jià)很小,不過從&str轉(zhuǎn)換到String涉及到分配內(nèi)存。除非必要,沒有理由這樣做!

索引(Indexing)

因?yàn)樽址怯行TF-8編碼的,它不支持索引:

let s = "hello";

println!("The first letter of s is {}", s[0]); // ERROR!!!

通常,用[]訪問一個(gè)數(shù)組是非??斓摹2贿^,字符串中每個(gè)UTF-8編碼的字符可以是多個(gè)字節(jié),你必須遍歷字符串來找到字符串的第N個(gè)字符。這個(gè)操作的代價(jià)相當(dāng)高,而且我們不想誤導(dǎo)讀者。更進(jìn)一步來講,Unicode實(shí)際上并沒有定義什么“字符”。我們可以選擇把字符串看作一個(gè)串獨(dú)立的字節(jié),或者代碼點(diǎn)(codepoints):

let hachiko = "忠犬ハチ公";

for b in hachiko.as_bytes() {
    print!("{}, ", b);
}

println!("");

for c in hachiko.chars() {
    print!("{}, ", c);
}

println!("");

這會打印出:

229, 191, 160, 231, 138, 172, 227, 131, 143, 227, 131, 129, 229, 133, 172,
忠, 犬, ハ, チ, 公,

如你所見,這有比char更多的字節(jié)。

你可以這樣來獲取跟索引相似的東西:

# let hachiko = "忠犬ハチ公";
let dog = hachiko.chars().nth(1); // kinda like hachiko[1]

這強(qiáng)調(diào)了我們不得不遍歷整個(gè)char的列表。

切片(Slicing)

你可以使用切片語法來獲取一個(gè)字符串的切片:

let dog = "hachiko";
let hachi = &dog[0..5];

注意這里是字節(jié)偏移,而不是字符偏移。所以如下代碼在運(yùn)行時(shí)會失?。?/p>

let dog = "忠犬ハチ公";
let hachi = &dog[0..2];

給出如下錯(cuò)誤:

thread '<main>' panicked at 'index 0 and/or 2 in `忠犬ハチ公` do not lie on
character boundary'

連接(Concatenation)

如果你有一個(gè)String,你可以在它后面接上一個(gè)&str

let hello = "Hello ".to_string();
let world = "world!";

let hello_world = hello + world;

不過如果你有兩個(gè)String,你需要一個(gè)&

let hello = "Hello ".to_string();
let world = "world!".to_string();

let hello_world = hello + &world;

這是因?yàn)?code>&String可以自動轉(zhuǎn)換為一個(gè)&str。這個(gè)功能叫做[Deref轉(zhuǎn)換](Deref coercions Deref強(qiáng)制多態(tài).md)。

上一篇:錯(cuò)誤處理