转载

Rust 学习笔记(八) - String和 &str - 神佑之园

Rust 学习笔记(八) — String和 &str

神眷之子 四月 24, 2016 科技 No Comments

对字符串的处理一直是各个语言的重点,因为平常我们打交道最多的,除了数组,基本上就是字符串了。Java里面有不可变的String,可变的StringBuilder,Python的更简单,一串字符用引号引起来就是字符串,并且自带Iterator可以立刻的写出

hello = "hello word"  for sample in hello: print(sample)  

但是Rust就又不走寻常路了,把字符串分成 字符串碎片 &str 和 字符串 String 两类。这是前段时间一直在折磨我的一个rust特性。


fn print_msg(msg: String) { println!("here you are {}", msg); } fn main() { let message = " hello!"; print_msg(msg); }

然后编译器愉快的报错了,说你明明告诉我msg是一个String啊,为什么传给我的参数是一个 &str? 于是只能非常崩溃的加上 let message = "hello!".to_string(); 方才能通过编译。更有甚者:


fn main() { let hello: String = "hello".to_string(); let world: String = "world".to_string(); let hello_world = hello + world; }

编译器继续报错啦,因为两个String不能相加,必须要把其中一个碎片化,改成


let hello_world = hello + &world;

顺利通过编译。

为什么非要简单问题复杂化呢,别的语言一个String打天下也没什么问题啊,随着理解的深入,我渐渐的发现这么区分还真是有道理的。我们拿一个可以正确通过编译的例子来说:


fn main() { let hello: String = "hello".to_string(); let world: &str = "world"; let hello_world: String = hello + world; }

首先,&str 顾名思义是一个引用,因为&号说明了一切。也就是说 let world: &str = "world"; 这句话里,heap上有一块内存写入了”world”这个字符串,然后创建了一个只读的引用指向world这个变量,也就是说world并不 拥有 这个字符串,而只是引用了这个字符串,对world变量进行的操作,不会销毁heap上的“world”。比如我们在末尾加上一句: println!({:?}, world); 我们会发现依然会正确的打印出world字符串。操作&str不牵扯所有权的转移,这也符合我们平常使用字符串的习惯,这也是为什么Rust系统默认双引号创建的是 &str 而不是 String的原因。

而String这个类型和Vec或者其他更加复杂的struct并无本质区别,拿hello这个变量做例子,它是一个直接绑定在hello变量上的一块内存区域,也就是说hello这个变量拥有对String这个object的所有权,里面有“hello”这个字串,使用之后所有权转移,内存或者被释放,或者被move到其他的地方去,总之如果你再想访问hello是没有了,在我们上面这个例子里面,如果加上 println!("{:?}", hello); , 编译器会抱怨:使用了moved value “hello”. 而这并不符合我们日常对字串的使用。我们一般不太希望字串操作之后,原始的字串迅速被销毁。想想你建立一个hashmap,里面用字串做key,然后你拿出来赋了一个值,之后hashmap里面这个entry就被删除了…………

Rust严格的所有权使得对String和&str的区分成为了必要,如果这个问题挪到struct里面,更加的有意思,也更加微妙,时间有点紧,且听下回分解。

原文  https://blog.scislab.com/zh/2016/04/学习笔记之八-string和-str/
正文到此结束
Loading...