Rust入门进阶系列 | 理解Rust中的 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<u8>,
}

我们可以通过多种方式创建一个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的索引操作

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

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

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());
}

 

四、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);
}

这段代码会出现报错

对于上面一段代码 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();
}

 
    
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Rust的字符串是一个动态可变的数据类型,称为`String`。通过`String`类型,我们可以创建、修改、操作和处理字符串。 在Rust,有多种方法可以创建`String`。最常见的方法是使用`format!`宏来构建一个字符串,或者通过`to_string()`方法将其他类型转换为字符串。 ```rust let hello = "Hello".to_string(); let world = String::from("World"); let message = format!("{} {}", hello, world); ``` `String`类型与基本类型(`&str`)之间可以互相转换。使用`&`运算符可以获取`String`类型的引用,而通过使用像`to_string()`这样的方法,可以将`&str`转换为`String`。 ```rust let hello: &str = "Hello"; let hello_string: String = hello.to_string(); ``` 在Rust,可以使用`+`运算符将两个字符串连接起来,或使用`push_str`方法将一个字符串附加到另一个字符串上。这两种方法都会创建一个新的`String`对象。 ```rust let hello = String::from("Hello"); let world = String::from("World"); let hello_world = hello + &world; // 可以使用&运算符获取world的引用 ``` 当需要修改字符串时,Rust提供了很多有用的方法。我们可以使用`replace`方法来替换子字符串,使用`trim`方法来去除字符串两侧的空格,使用`split`方法将字符串拆分为多个部分等等。 ```rust let message = String::from("Hello World"); let replaced = message.replace("World", "Rust"); let trimmed = message.trim(); let mut parts = message.split_whitespace(); ``` 总之,Rust的`String`类型提供了许多功能强大且安全的方法来创建、操作和处理字符串。它的灵活性和性能使得在处理字符串时可以更加方便、高效地进行操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

松_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值