Rust入门进阶系列 | 理解Rust中的 String 和 &str

Rust入门进阶系列 | 理解Rust中的 String 和 &str_第1张图片

目录

前言

一、String的理解

二、什么是&str

 三、String和&str

四、String 还是 &str ?,该如何选择?


前言

当你开始学习 Rust 编程时,你可能经常遇到字符串使用问题的编译错误,经常搞不懂String和&str什么时候使用,出现了又如何解决。

Rust 的字符串设计与大多数具有单一字符串类型的编程语言有所不同。Unicode 安全的字符串设计和独特的所有权机制都使 Rust 中的字符串对于初学者来说在某种程度上是违反直觉的。

这篇文章我们简单说一下String和&str是什么意思,代表什么含义,尤其是在什么时候使用哪一个类型。

理解这篇文章中涉及的概念将使您能够以更有效的方式使用 Rust 中的字符串,同时也可以更好地理解其他人的代码,因为您将更好地了解为什么做出有关字符串处理的某些决定的原因。


一、String的理解

String 是一个需要动态分配大小的,其大小在代码编译时是未知的,内部的容量是可以随时根据需要变化的。

从String的源码可以看到他的结构如下:

#[derive(PartialOrd, Eq, Ord)]
#[cfg_attr(not(test), rustc_diagnostic_item = "String")]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct String {
    vec: Vec,
}

我们可以通过多种方式创建一个String:

let s = String::new()         // An empty String
let s = "Hello".to_string();
let s = String::from("world");
let s: String = "also this".into();

Rust 的字符和字符串类型是围绕 Unicode 设计的。 String不是 ASCII 字符序列,而是 Unicode 字符序列。Rust 类型是一个包含 Unicode 代码的 32 位值。 String使用 UTF-8 将 Unicode 字符编码转为vec。

let hello = String::from("こんにちは");
let hello = String::from("안녕하세요");
let hello = String::from("你好");

字符串中的.len()方法将返回以字节为单位的长度,而不是以字符为单位的长度。

assert_eq!("ಠ_ಠ".len(), 7);
assert_eq!("ಠ_ಠ".chars().count(), 3);

而字符串都没有index的索引操作

Rust入门进阶系列 | 理解Rust中的 String 和 &str_第2张图片

由于 UTF-8 是一种可变宽度的编码格式,因此字符串中的每个 ASCII 字符都存储在一个字节中,而其他字符可能占用多个字节。因此,在这种格式中,字符串字节的索引并不总是与有效的 Unicode 标量值相关。

Rust入门进阶系列 | 理解Rust中的 String 和 &str_第3张图片

如果想更方便地修改一个 String中的特定字节,则需要将其转换为vec:

fn set_char_inplace(text: &mut String, index: usize, c: char) {
    let bytes = unsafe { text.as_mut_vec() };
    bytes[index] = c as u8;
}

String可以从另一个方面理解成是一个字节的集合和一些集合的操作方法,存储了一串UTF-8编码的文本。

Rust 中的字符串保证是有效的 UTF-8,可以参考这篇文章的说明Rust为什么要使用UTF-8UTF-8 Everywhere (utf8everywhere.org)

二、什么是&str

str是Rust语言原始类型,也可以称为string slice,通常以借用的方式出现&str

let a: &str = "hello rust";
//it is same with below
let a: 'static str = "hello rust";
// 'static 是一个静态变量,生命周期贯穿整个代码执行
// &str 一般不可直接修改

通常这种都叫做胖指针,胖指针一般有两部分组成:指向bytes的指针和长度

可以通过as_ptr()方法和len()方法获取对应的值

let story = "Hello world";

let ptr = story.as_ptr();
let len = story.len();

 三、String和&str

Rust中的String和&str,就像C++中的std::string和char*:

  • String 拥有所有内存相关的操作,可以对字符串做很多的处理
  • &str 有包含长度

&str包含长度,是因为在操作时候可以使用&str获取到String中的任意一段的内容,例如:

pub fn main() {
    let s: String = String::from("hello");
    let a: &str = &s;
    let b: &str = &s[1..3];
    let c = "beaf";
    println!("a: {}, len: {}", a, a.len());
    println!("b: {}, len: {}", b, b.len());
    println!("c: {}, len: {}", c, c.len());
}

 Rust入门进阶系列 | 理解Rust中的 String 和 &str_第4张图片

四、String 还是 &str ?,该如何选择?

 一般要遵循两个原则:

  • 当您需要拥有内存时使用String。例如,您在函数中创建了一个字符串,需要各自操作,需要返回它。

  • 当您需要对 String 变量(或代码中的字符串文本)的不可变引用时使用&str,一般只是引用不会对其修改或其他操作

请记住,当String您将其作为参数传递给函数时,Rust 将遵循move语义原则

fn func(a: String) {
    println!("{}", a);
}

pub fn main() {
    let v = String::from("hello");
    func(v);
    println!("v: {}", v);
}

这段代码会出现报错

Rust入门进阶系列 | 理解Rust中的 String 和 &str_第5张图片

对于上面一段代码 v 已经move到了func里面,在Rust中可以使用Rust语言的 deref coercing

将&String 转成 &str。如果使用&str,可以修改成:

fn func(a: &str) {
    println!("{}", a);
}

pub fn main() {
    let v = String::from("hello");
    func(&v);
    println!("v: {}", v);
}

所以当我们在设计一个API或者方法如果只是对字符串的只读操作,最好还是使用&str

但是当你需要修改一个String的内容时,并不能通过&mut str修改 ,虽然确实存在,但它不是很有用,因为切片无法重新分配其引用。当你需要修改字符串的内容时,需要&mut String作为参数传递,就像上面提到的set_char_inplace例子一样。

在struct中使用字符串时,通常需要修改或操作字符串,最好使用String,但是也可以使用str,只是需要注意rust里面的生命周期是否正确

struct Person<'a> {
    name: &'a str,
}
impl<'a> Person<'a> {
    fn greet(&self) {
        println!("Hello, my name is {}", self.name);
    }
}
fn main() {
    let person = Person { name: "Coder's Cat" };
    person.greet();
}

 
      

你可能感兴趣的:(rust,rust,开发语言,后端)