Rust面试题及详细答案120道(58-65)-- 集合类型

前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,SQL,Linux... 。

前后端面试题-专栏总目录

文章目录

一、本文面试题目录

58. 简述Rust标准库中的主要集合类型(VecStringHashMap等)

Rust标准库提供了多种集合类型,用于存储和管理数据集合,它们都在堆上分配内存,且具有不同的特性和适用场景。主要集合类型包括:

  • Vec<T>(动态数组)

    • 原理:连续存储相同类型T的元素,支持动态扩容,内部基于Vec<u8>实现。
    • 特点:随机访问效率高(O(1)),适合存储有序、可重复的元素。
  • String(字符串)

    • 原理:UTF-8编码的可变字符串,内部本质是Vec<u8>,但保证数据符合UTF-8标准。
    • 特点:支持字符串拼接、修改,适合处理文本数据。
  • HashMap<K, V>(哈希表)

    • 原理:基于哈希函数存储键值对(K为键,V为值),通过键快速查找值。
    • 特点:插入和查询效率高(平均O(1)),但元素无序,K需实现HashEq trait。
  • BTreeMap<K, V>(有序映射)

    • 原理:基于B树实现的键值对集合,元素按键的顺序排序。
    • 特点:支持范围查询,插入和查询效率为O(log n),K需实现Ord trait。
  • HashSet<T>(哈希集合)

    • 原理:基于HashMap实现,仅存储键(值为单元类型()),确保元素唯一。
    • 特点:判断元素是否存在效率高(平均O(1)),T需实现HashEq trait。
  • BTreeSet<T>(有序集合)

    • 原理:基于BTreeMap实现,元素唯一且按顺序存储。
    • 特点:支持范围查询和排序,T需实现Ord trait。
  • LinkedList<T>(双向链表)

    • 原理:节点通过指针连接的双向链表,不连续存储。
    • 特点:插入和删除首尾元素效率高(O(1)),但随机访问效率低(O(n)),一般不推荐优先使用。

59. Vec<T>的基本操作(创建、添加、删除、访问元素),如何避免越界访问?

Vec<T>是Rust中最常用的动态数组类型,支持多种操作,同时需注意避免越界访问以保证安全性。

基本操作示例:
rust 复制代码
fn main() {
    // 1. 创建Vec
    let mut v1: Vec<i32> = Vec::new(); // 空Vec
    let v2 = vec![1, 2, 3]; // 使用vec!宏初始化
    let v3 = (0..5).collect::<Vec<i32>>(); // 从迭代器收集

    // 2. 添加元素(push/apppend)
    v1.push(4);
    v1.push(5);
    let mut v4 = vec![6, 7];
    v1.append(&mut v4); // 合并v4到v1(v4会被清空)
    println!("v1: {:?}", v1); // [4, 5, 6, 7]

    // 3. 访问元素
    let third = &v2[2]; // 索引访问(越界会panic)
    println!("v2[2]: {}", third); // 3

    let opt = v2.get(1); // get方法(返回Option<&T>,越界返回None)
    if let Some(val) = opt {
        println!("v2[1]: {}", val); // 2
    }

    // 4. 删除元素
    let last = v1.pop(); // 移除最后一个元素(返回Option<T>)
    println!("pop: {:?}", last); // Some(7)
    v1.remove(0); // 移除索引0的元素(返回被移除的值)
    println!("v1 after remove: {:?}", v1); // [5, 6]
}
避免越界访问的方法:
  1. 使用get方法 :返回Option<&T>,越界时返回None,可通过if letmatch安全处理。
  2. 检查长度 :通过v.len()判断索引是否在有效范围内(0 <= index < v.len())。
  3. 迭代器访问 :使用for item in &v遍历元素,无需手动管理索引。
  4. 模式匹配 :结合Vecis_empty方法和范围判断,避免访问空数组。

60. String&str的关系,String的内部结构是什么(提示:Vec<u8>)?

String&str的关系:
  • String :是一个可变的、拥有所有权的UTF-8字符串,数据存储在堆上,支持修改(如拼接、插入等)。
  • &str :是一个不可变的字符串切片&[u8]的特化),指向String或静态字符串(&'static str)中的一段连续UTF-8数据,不拥有所有权。
  • 关系:String可以通过&s(或s.as_str())转换为&str,而&str可以通过to_string()String::from()转换为String(会复制数据)。
String的内部结构:

String本质上是对Vec<u8>的封装,其内部结构包含三部分(与Vec一致):

  • 指针:指向堆上存储的UTF-8字节数据。
  • 长度 :当前字符串的字节数(len()方法返回)。
  • 容量 :堆上分配的总字节数(capacity()方法返回,大于等于长度)。

示例:

rust 复制代码
fn main() {
    let s = String::from("hello");
    // String -> &str
    let slice: &str = &s;
    println!("slice: {}", slice); // hello

    // &str -> String
    let s2 = slice.to_string();
    println!("s2: {}", s2); // hello

    // 查看String的内部字节(UTF-8编码)
    let bytes = s.as_bytes(); // &[u8]
    println!("bytes: {:?}", bytes); // [104, 101, 108, 108, 111](对应"hello"的ASCII码)
}

61. 如何处理UTF-8编码的字符串?为什么String不能直接通过索引访问字符?

处理UTF-8编码的字符串:

UTF-8是一种可变长度的Unicode编码(1-4字节表示一个字符),Rust的String&str均采用UTF-8编码。常见处理方式包括:

  1. 迭代字符 :使用chars()方法获取字符迭代器(每个元素是char类型)。
  2. 按字节处理 :使用as_bytes()获取字节切片(&[u8]),适合处理原始字节。
  3. 获取子串 :使用get()split_at()方法,需确保分割点在UTF-8字符边界上。

示例:

rust 复制代码
fn main() {
    let s = String::from("你好,world");

    // 迭代字符(char)
    for c in s.chars() {
        print!("{} ", c); // 你 好 , w o r l d 
    }
    println!();

    // 按字节处理(注意:中文字符占3字节)
    let bytes = s.as_bytes();
    println!("bytes: {:?}", bytes); // [228, 189, 160, 229, 165, 189, ...]

    // 安全获取子串(从索引0到6,对应"你好")
    if let Some(sub) = s.get(0..6) {
        println!("sub: {}", sub); // 你好
    }
}
为什么String不能直接通过索引访问字符?
  • UTF-8的可变长度特性 :一个char可能占1-4字节(如英文字母1字节,中文3字节),索引访问的是字节位置,而非字符位置,可能导致获取到不完整的字符(如半个中文字符)。
  • 安全性设计 :Rust为避免无效的UTF-8数据访问,禁止直接通过索引访问String的字符,强制使用chars()get()等安全方法。

62. HashMap<K, V>的使用场景,如何插入、查询、删除键值对?

HashMap<K, V>的使用场景:
  • 需通过唯一键快速查询值(如字典、缓存、用户ID与信息映射)。
  • 元素无序且对排序无要求。
  • 插入和查询操作频繁,且希望平均时间复杂度为O(1)。
基本操作示例:
rust 复制代码
use std::collections::HashMap;

fn main() {
    // 创建HashMap
    let mut map: HashMap<&str, i32> = HashMap::new();

    // 1. 插入键值对(insert返回旧值的Option)
    map.insert("one", 1);
    let old_val = map.insert("one", 100); // 覆盖旧值"one"
    println!("old_val: {:?}", old_val); // Some(1)

    // 2. 查询值(get返回Option<&V>)
    let val = map.get("one");
    if let Some(v) = val {
        println!("one: {}", v); // 100
    }

    // 3. 检查键是否存在
    if map.contains_key("two") {
        println!("two exists");
    } else {
        println!("two not exists"); // 执行此分支
    }

    // 4. 删除键值对(remove返回被删除值的Option)
    let removed = map.remove("one");
    println!("removed: {:?}", removed); // Some(100)
    println!("map after remove: {:?}", map); // {}
}
其他常用操作:
  • 遍历for (k, v) in &map 遍历键值对。
  • 更新值entry API(如map.entry("key").or_insert(0),不存在则插入默认值)。

63. BTreeMapHashMap的区别,何时选择BTreeMap

BTreeMapHashMap的区别:
特性 HashMap<K, V> BTreeMap<K, V>
内部实现 哈希表 B树(有序数据结构)
元素顺序 无序 按键的Ord trait排序
插入/查询复杂度 平均O(1),最坏O(n) O(log n)
键的约束 K: Hash + Eq K: Ord
范围查询支持 不支持 支持(如range(a..b)
内存占用 较高(哈希表需预留空间) 较低(B树结构紧凑)
何时选择BTreeMap
  1. 需要元素按键排序(如排行榜、字典序输出)。
  2. 需要范围查询(如查找键在[a, b]之间的所有元素)。
  3. 键类型实现Ord但不实现Hash(如自定义类型未实现Hash)。
  4. 对内存占用较敏感,且插入/查询频率适中(O(log n)可接受)。

示例(BTreeMap范围查询):

rust 复制代码
use std::collections::BTreeMap;

fn main() {
    let mut btree_map = BTreeMap::new();
    btree_map.insert(3, "three");
    btree_map.insert(1, "one");
    btree_map.insert(2, "two");

    // 自动按键排序
    println!("btree_map: {:?}", btree_map); // {1: "one", 2: "two", 3: "three"}

    // 范围查询(键1到2)
    let range = btree_map.range(1..=2);
    for (k, v) in range {
        println!("{}: {}", k, v); // 1: one, 2: two
    }
}

64. HashSetBTreeSet的特点,如何判断元素是否存在?

HashSet<T>的特点:
  • 基于HashMap<T, ()>实现,存储唯一元素(无键值对,仅值)。
  • 元素无序,T需实现Hash + Eq trait。
  • 插入、删除、判断存在的平均复杂度为O(1)。
  • 适合快速去重和存在性检查,对顺序无要求。
BTreeSet<T>的特点:
  • 基于BTreeMap<T, ()>实现,元素唯一且按Ord trait排序。
  • T需实现Ord trait。
  • 插入、删除、判断存在的复杂度为O(log n)。
  • 支持范围查询和有序遍历,适合需要排序或范围操作的场景。
判断元素是否存在的方法:

两种集合均通过contains方法判断元素是否存在,返回bool

示例:

rust 复制代码
use std::collections::{HashSet, BTreeSet};

fn main() {
    // HashSet
    let mut hash_set = HashSet::new();
    hash_set.insert("apple");
    hash_set.insert("banana");
    println!("HashSet has 'apple'? {}", hash_set.contains("apple")); // true

    // BTreeSet
    let mut btree_set = BTreeSet::new();
    btree_set.insert(3);
    btree_set.insert(1);
    btree_set.insert(2);
    println!("BTreeSet has 2? {}", btree_set.contains(&2)); // true
    println!("BTreeSet elements: {:?}", btree_set); // {1, 2, 3}(有序)
}

65. 什么是"切片(Slice)"?&[T]&str的关系是什么?

切片(Slice)的定义:

切片是一种不拥有所有权的引用类型 ,用于指向集合中一段连续的元素,格式为&[T](泛型切片)或&str(字符串切片)。它不存储数据,仅包含指针 (指向数据起始位置)和长度(元素数量),因此长度在编译时不确定,但访问时会检查边界以避免越界。

切片的特点:
  • 不可变切片(&[T]):不能修改指向的元素。
  • 可变切片(&mut [T]):可以修改指向的元素,但需遵守借用规则(同一时间只能有一个可变引用)。
  • 常用于安全访问集合的部分数据,无需复制整个集合。
&[T]&str的关系:
  • &str&[u8]特化版本,专门用于表示UTF-8编码的字符串切片,保证数据符合UTF-8标准。
  • &[T]是通用的切片类型,可指向任何连续存储的同类型元素(如Vec<T>、数组[T; N])。
  • 两者均为切片,结构相同(指针+长度),但&str有额外的UTF-8有效性约束。

示例:

rust 复制代码
fn main() {
    // 数组切片(&[T])
    let arr = [1, 2, 3, 4, 5];
    let slice: &[i32] = &arr[1..4]; // 指向arr的索引1到3(元素2,3,4)
    println!("array slice: {:?}", slice); // [2, 3, 4]

    // 字符串切片(&str)
    let s = String::from("hello world");
    let str_slice: &str = &s[0..5]; // 指向"hello"
    println!("string slice: {}", str_slice); // hello

    // &str本质是&[u8]的特化(但保证UTF-8)
    let bytes: &[u8] = str_slice.as_bytes();
    println!("bytes: {:?}", bytes); // [104, 101, 108, 108, 111]
}

二、120道Rust面试题目录列表

文章序号 Rust面试题120道
1 Rust面试题及详细答案120道(01-10)
2 Rust面试题及详细答案120道(11-18)
3 Rust面试题及详细答案120道(19-26)
4 Rust面试题及详细答案120道(27-32)
5 Rust面试题及详细答案120道(33-41)
6 Rust面试题及详细答案120道(42-50)
7 Rust面试题及详细答案120道(51-57)
8 Rust面试题及详细答案120道(58-65)
9 Rust面试题及详细答案120道(66-71)
10 Rust面试题及详细答案120道(72-80)
11 Rust面试题及详细答案120道(81-89)
12 Rust面试题及详细答案120道(90-98)
13 Rust面试题及详细答案120道(99-105)
14 Rust面试题及详细答案120道(106-114)
15 Rust面试题及详细答案120道(115-120)
相关推荐
还是大剑师兰特19 小时前
ruoyi‑vue3 添加【车辆管理】菜单组完整方案
大剑师·若依系统
还是大剑师兰特4 天前
若依(ruoyi)前后台系统从零到一部署配置(坑点带解析)
大剑师
还是大剑师兰特5 天前
ideaIC‑2024.1.7(社区版zip绿色解压版)完整使用教程
大剑师·idealc
还是大剑师兰特7 天前
MySQL8.0 Windows完整安装教程
windows·mysql·大剑师
还是大剑师兰特1 个月前
VSCode 插件冲突导致崩溃、卡死、假死 全套根治方案
vscode·大剑师
还是大剑师兰特1 个月前
ThinkPad T490 Windows 自动修复失败完整解决步骤
windows·大剑师·t490
还是大剑师兰特1 个月前
二年级奥数(12) : 巧填符号+竖式数字谜
大剑师·二年级奥数
还是大剑师兰特1 个月前
二年级奥数(9):鸡兔同笼+ 归一问题
大剑师·二年级奥数
还是大剑师兰特1 个月前
二年级奥数(7):移多补少8题 + 和差入门8题 + 年龄问题8题
大剑师·二年级奥数
还是大剑师兰特2 个月前
Vite6 + Vue3 + TS 完整版标准配置模板
vite·大剑师·vite6配置