MST

星途 面试题库

面试题:Rust中不同数据类型的内存布局及所有权机制

请详细阐述Rust中`String`和`&str`的区别,包括它们的内存布局、所有权机制以及在实际编程中如何正确选择使用。同时,举例说明在哪些场景下会发生`String`到`&str`的自动解引用。
10.4万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

1. 内存布局

  • String
    • String是一个可增长的字符串类型,它在堆上分配内存。其内存布局包含三个部分:指向堆上数据的指针,记录字符串长度的usize类型变量,以及记录容量(即当前分配的堆内存大小)的usize类型变量。例如,当创建一个String对象let s = String::from("hello");,指针指向包含'h', 'e', 'l', 'l', 'o', '\0'的堆内存区域,长度为5,容量至少为5(可能更大以适应后续增长)。
    • 由于String拥有堆上的数据,它负责数据的分配和释放,当String对象离开作用域时,堆上的数据会被释放。
  • &str
    • &str是字符串切片,它是一个指向UTF - 8编码字符串数据的不可变引用。其内存布局很简单,就是一个指向字符串数据的指针和一个记录字符串长度的usize类型变量。例如,当有一个字符串字面量let s: &str = "world";,指针指向存储'w', 'o', 'r', 'l', 'd'的内存区域(字符串字面量存储在程序的只读数据段),长度为5。
    • &str并不拥有它所指向的数据,数据的生命周期由其所有者决定。

2. 所有权机制

  • String
    • String拥有它所包含的字符串数据的所有权。当一个String对象被赋值给另一个变量或者作为参数传递给函数时,所有权会发生转移。例如:
    let s1 = String::from("rust");
    let s2 = s1; // s1的所有权转移给s2,此时s1不再有效
    
    • String对象离开作用域时,Rust的Drop trait会被自动调用,释放其在堆上分配的内存。
  • &str
    • &str不拥有数据的所有权,它只是借用数据。这意味着多个&str切片可以同时指向同一个字符串数据,只要数据的所有者还存在。例如:
    let s = String::from("hello");
    let slice1: &str = &s;
    let slice2: &str = &s[0..3];
    
    • 由于&str只是借用,所以不会影响数据所有者的生命周期,也不会导致数据的释放。

3. 实际编程中如何正确选择使用

  • 使用String的场景
    • 当需要对字符串进行修改、增长或需要拥有字符串数据的所有权时,应使用String。比如,从用户输入读取字符串,因为用户输入的长度是不确定的,需要一个可增长的字符串类型。
    use std::io;
    let mut input = String::new();
    io::stdin().read_line(&mut input).expect("Failed to read line");
    
    • 当需要将字符串传递给函数,并期望函数对其进行修改或拥有所有权时,使用String
  • 使用&str的场景
    • 当只需要读取字符串内容,并且不需要拥有字符串数据的所有权时,使用&str。比如,函数参数如果只是用于读取字符串内容,使用&str作为参数类型可以避免不必要的所有权转移和数据复制。
    fn print_str(s: &str) {
        println!("The string is: {}", s);
    }
    let s = "example";
    print_str(s);
    
    • 当需要处理字符串字面量时,字符串字面量的类型就是&str,所以在这种情况下自然使用&str

4. String&str的自动解引用

  • 函数调用场景
    • 当函数参数类型为&str,而传递的是String类型的变量时,会发生自动解引用。例如:
    fn print_str(s: &str) {
        println!("The string is: {}", s);
    }
    let s = String::from("auto deref");
    print_str(&s); // 这里`&s`(类型`&String`)自动解引用为`&str`
    
  • 方法调用场景
    • String对象调用一个接收&str参数的方法时,会自动解引用。例如:
    let s = String::from("rust");
    let contains_r = s.contains('r'); // `s.contains`方法接收`&str`,这里`&s`自动解引用为`&str`