4. Rust中的String、str与所有权

一、前言

前面的章节介绍了Rust中的很多基本基本数据结构,并且常常用到String这个类型,因为它与Rust中的一个核心概念有关:所有权

所以前面一直没有对它进行解答,而本文的目的,就是深究这个String的底层原理,并以此作为媒介,引出所有权的概念。

二、字符串浅析

从比较宏观的角度去看你经常接触到的软件可以发现,对于控制台程序,你看到的只有字符。

而对于有图形界面的软件(GUI),除了图案、就是各种文字了(字符串)。

从这你便能看出字符串的重要性:无论什么程序,基本都离不开字符串的使用

在rust中使用字符串,可以用以下代码:

fn main() {
    let s=String::from("hello world");
    println!("{}", s);
}

方式是用String这个crate(箱子、包)中的from函数,来从一个字面量hello world得到一个字符串String类型:

image.png

那看到这里你可能觉得有些奇怪了,明明我直接等于一个字符串字面量也可以呀!

比如像下面这样:

fn main() {
    let s="hello world";
    println!("{}", s);
}

这确实是没问题的,但如果你看到编译器自动推导的类型就会发现,此刻的s已经不是String类型了

image.png

是不是觉得很奇怪!这里的是&'static str类型,并不是字符串String类型。

这里我们先不管这两个类型之间的区别,仅从现象上看就能说明,字符串字面量的类型就是&' static str

因为编译器推断s的类型是通过右边的字面量来推断的,既然是s&' static str类型,那么右边的字面量肯定也是&'static str类型才对。

那么就此可以推断出,String::from这个函数的作用就是将一个字符串字面量&'static str类型转换为String类型。

因为经由这个函数之后,编译器就推断出sString类型了。

注意这个'static的前缀,它实际上并不是类型、而是生命周期标注,static意味着它是静态的、全局可用的,所以其更简洁的类型表示实际上是&str

那么这两种类型有什么区别呢?

目前来看,最直观的区别就是:String类型是可变的,而&str类型是不可变的

比如下面的代码:

fn main() {
    let mut s=String::from("hello world");
    s=s+"world"; //正确
    println!("{}", s);
    let mut s="hello world"; //用到变量隐藏特性
    s=s+"test"; //错误
    println!("{}", s);
}

即使你添加了mut关键字,对于&str类型,仍然是不可变的。

对于字符串的+运算符,会将后面的字符串追加到第一个字符串并返回,比如s+"test",就是尝试将"test"追加到s之后、并返回拼接的结果,如果s不可变,那么就会拼接失败。

原因在于mut指代的是s本身可变,比如你完全可以让s等于一个新的字符串:

let mut s="hello";
s="world"; //s变量本身可变,所以可以重新赋值为新的字符串。
s+"world"; //+运算符的底层逻辑是在s所指向的字符串后拼接、追加新的字符串,由于字符串s的类型为&str,不可变,所以失败

下面我们就要开始分别深度探讨两个几个问题:

  • String类型与&str类型到底代表什么意思?
  • 为什么两者会有这个特性?

只要搞懂了这两个问题,你对于rust的设计理念认知便又进了一大步,这对于之后的学习是很有帮助的。

因为除了字符串之外,其它很多地方都会用到这个特性,这并非是字符串所独有的。

三、&str与String

首先看到第一个问题: String类型与&str类型到底代表什么意思?

我们要知道&这个符号是可以剥离出来的,实际上应该是:Stringstr

如何你对C++熟悉的话,那么可以认为&基本就等价于C++中的引用。

其中String代表一个可独立操作的字符串,也就是说,它拥有一个字符串的所有权力,比如下面这段代码:

let mut s=String::from("hello world");

此时变量s就完全拥有了操作这个字符串"hello world"的权利,增、删、改、查均可以,一旦你这个变量s没有了,那么"hello world"这个字符串同样也会在消失,两者是彻底绑定在一起的。

str这个类型,其意义仅在于你可以用它,比如打印,遍历,查看等等,除此之外,它对字符串本身根本做不了任何改变,因为它不拥有这个字符串。

let mut a="hello world";

比如上面这段代码,即使a变量消失了,这个字符串"hello world"实际上仍然在内存中,并没有消失,因为a并不拥有这个字符串。

所以如果你想要修改一个str类型的变量,你就必须先将其转换为String类型

    let mut a="hello world";
    let mut s=a.to_string();
作者:余识
全部文章:0
会员文章:0
总阅读量:0
c/c++pythonrustJavaScriptwindowslinux