还有一种不领有所属权的数据类型为:slice。

slice使咱们能够援用汇合中元素的间断序列而不是整个汇合。

一个小的编程问题:编写一个函数,该函数须要一个字符串并返回在该字符串中找到的第一个单词。 如果函数在字符串中找不到空格,则整个字符串必须是一个单词,因而应返回整个字符串。

fn first_word(s: &String) -> usize {    let bytes = s.as_bytes();    for (i, &item) in bytes.iter().enumerate() {        if item == b' ' {            return i;        }    }    s.len()}

在上例中:
1、因为咱们须要一一查看String元素,并查看值是否为空格,所以咱们将应用as_bytes办法将String转换为字节数组:let bytes = s.as_bytes();
2、咱们应用iter办法在字节数组上创立一个迭代器:for (i, &item) in bytes.iter().enumerate() {

3、在for循环内,咱们应用字节文字语法搜寻示意空格的字节。 如果找到空格,则返回地位。 否则,咱们应用s.len()返回字符串的长度:

    if item == b' ' {         return i;     } }  s.len()

4、当初,咱们能够找到字符串中第一个单词结尾的索引,但这是有问题的, 咱们将本人返回一个usize,然而在&String的上下文中,这只是一个有意义的数字。 换句话说,因为它是与String离开的值,因而无奈保障它在未来依然无效,所以将其革除:

fn main() {    let mut s = String::from("hello world");    let word = first_word(&s); // word将要获取到的值为5    s.clear(); // 这将清空字符串,使其等于""    // word依然在这里具备值5,然而没有更多的字符串可用于有意义地应用值5.这个词当初齐全有效!    }

该程序编译时没有任何谬误,如果在调用s.clear()之后应用word,也能够这样做。 因为word基本没有连贯到s的状态,所以word依然蕴含值5。咱们能够将值5与变量s一起应用以尝试提取第一个单词,但这将是一个bug,因为自从咱们保留了5到word中之后,s产生了变动。

不用放心word中的索引与s中的数据不同步,这既繁琐又容易出错! 如果咱们编写second_word函数,则治理这些索引的难度更大。 其签名必须如下所示:
fn second_word(s: &String) -> (usize, usize) {

当初,咱们正在跟踪起始索引和完结索引,并且咱们有更多的值是依据特定状态下的数据计算得出的,但与该状态齐全无关。 当初,咱们有三个不相干的变量须要放弃同步。

侥幸的是,Rust解决了这个问题:字符串切片。

String Slices

字符串切片是对字符串一部分的援用,它看起来像这样:

fn main() {    let s = String::from("hello world");    let hello = &s[0..5];    let world = &s[6..11];}
通过指定[starting_index..ending_index],咱们能够应用方括号内的范畴来创立切片,其中,starting_index是切片中的第一个地位,而ending_index是一个能够比切片中的最初一个地位大的数。 在外部,切片数据结构存储切片的起始地位和长度,它对应于ending_index减去starting_index。 因而,在let world =&s [6..11];的状况下,world将是一个切片,其中蕴含指向s的第7个字节(从1开始)的指针,其长度值为5。

应用Rust的..范畴语法,如果想从第一个索引(零)开始,则能够在两个句点之前删除该值。 换句话说,这些是相等的:

#![allow(unused_variables)]fn main() {    let s = String::from("hello");    let slice = &s[0..2];    let slice = &s[..2];}

同样,如果分片蕴含字符串的最初一个字节,则能够删除尾随数字。 这意味着这些是相等的:

#![allow(unused_variables)]fn main() {    let s = String::from("hello");    let len = s.len();    let slice = &s[3..len];    let slice = &s[3..];}

同样的能够删除这两个值以截取整个字符串。 所以这些是相等的:

#![allow(unused_variables)]fn main() {    let s = String::from("hello");    let len = s.len();    let slice = &s[0..len];    let slice = &s[..];}

思考到所有这些信息后,让咱们重写first_word以返回切片。 示意“字符串切片”的类型写为&str:

fn first_word(s: &String) -> &str {    let bytes = s.as_bytes();    for (i, &item) in bytes.iter().enumerate() {        if item == b' ' {            return &s[0..i];        }    }    &s[..]}

同样的,返回切片也能够用于second_word函数:fn second_word(s: &String) -> &str {

然而仍旧有一个问题,就是s.clear,废话不多说:

fn first_word(s: &String) -> &str {    let bytes = s.as_bytes();    for (i, &item) in bytes.iter().enumerate() {        if item == b' ' {            return &s[0..i];        }    }    &s[..]}fn main() {    let mut s = String::from("hello world");    let word = first_word(&s);    s.clear(); // 出错!    println!("第一个单词是: {}", word);}$ cargo run   |16 |     let word = first_word(&s);   |                           -- immutable borrow occurs here17 | 18 |     s.clear(); // 出错!   |     ^^^^^^^^^ mutable borrow occurs here19 | 20 |     println!("第一个单词是: {}", word);   |                                       ---- immutable borrow later used hereerror: aborting due to previous errorFor more information about this error, try `rustc --explain E0502`.error: could not compile `ownership`.To learn more, run the command again with --verbose.

从借阅规定中回想起,如果咱们对某物有不可变的援用,那么咱们也不能采纳可变的援用。 因为clear须要截断String,因而须要获取可变的援用。 Rust不容许这样做,并且编译失败。 Rust不仅使咱们的API易于应用,而且还打消了编译时的一整类谬误!(解决该问题的办法为,将s.clear语句放到最初)

字符串文字是切片

let s = "Hello, world!";
s的类型是&str:它是指向二进制文件的特定点的切片。 这也是字符串文字不可变的起因。 &str是不可变的援用。

字符串切片作为参数
晓得能够宰割文字和字符串值后,这使咱们对first_word进行了另一项改良,这就是其签名:
fn first_word(s: &String) -> &str { 与其等价的写法是:fn first_word(s: &str) -> &str {

如果咱们有一个字符串切片,咱们能够间接传递它。 如果咱们有一个字符串,咱们能够传递整个字符串的一部分。 定义一个函数以采纳字符串切片而不是对String的援用使咱们的API更通用和有用,而不会失落任何性能:

fn first_word(s: &str) -> &str {    let bytes = s.as_bytes();    for (i, &item) in bytes.iter().enumerate() {        if item == b' ' {            return &s[0..i];        }    }    &s[..]}fn main() {    let my_string = String::from("hello world");    // first_word实用于`String`s的切片    let word = first_word(&my_string[..]);    let my_string_literal = "hello world";    // first_word实用于字符串文字的切片    let word = first_word(&my_string_literal[..]);    // 因为字符串文字*曾经是*字符串切片,所以它也能够应用,而无需应用切片语法!    let word = first_word(my_string_literal);}

Other Slices

其余类型的切片跟String类型的切片相似:

#![allow(unused_variables)]fn main() {    let a = [1, 2, 3, 4, 5];    let slice = &a[1..3];}

该切片的类型为&[i32]。 通过存储对第一个元素和长度的援用,它的工作形式与字符串切片雷同。

所属权总结:

所有权,借用和切片的概念可确保在编译时Rust程序中的内存平安。 Rust可让咱们管制内存应用状况,当数据所有者超出范围时,让数据所有者主动革除该数据意味着咱们不用再编写和调试额定的代码。