《前后端面试题
》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,SQL,Linux… 。
文章目录
- 一、本文面试题目录
-
-
- 1. 简述Rust的基本数据类型有哪些,与其他语言相比有何特点?
- 2. Rust中`let`和`let mut`的区别是什么?为什么默认不可变?
- 3. 解释Rust的标量类型(Scalar Types),包括整数、浮点数、布尔值和字符的具体分类。
- 4. 什么是复合类型(Compound Types)?举例说明元组(Tuple)和数组(Array)的用法及区别。
- 5. Rust中字符串有`String`和`&str`两种类型,它们的区别是什么?如何相互转换?
- 6. 解释`Option<T>`枚举的作用,它解决了什么问题?与其他语言的`null`有何不同?
- 7. `Result<T, E>`枚举的用途是什么?如何处理其中的错误(使用`match`、`if let`或`?`运算符)?
- 8. 什么是范围(Range)?`..`和`..=`有何区别?举例说明其在循环中的应用。
- 9. Rust的注释有哪几种类型?文档注释(`///`和`//!`)的作用是什么?
- 10. 解释Rust的变量遮蔽(Shadowing),与`mut`有何区别?
-
- 二、120道Rust面试题目录列表
一、本文面试题目录
1. 简述Rust的基本数据类型有哪些,与其他语言相比有何特点?
Rust的基本数据类型可分为标量类型和复合类型:
- 标量类型:包括整数(如
i32
、u64
)、浮点数(f32
、f64
)、布尔值(bool
)、字符(char
,占4字节,支持Unicode)。 - 复合类型:包括元组(
tuple
,固定长度)、数组(array
,固定长度且元素类型相同)。
特点:
- 强类型且类型安全,编译期严格检查类型匹配。
- 无隐式类型转换(如
i32
不能直接转为i64
,需显式使用as
)。 - 字符类型默认支持Unicode(如可表示中文、 emoji),而部分语言(如C)的
char
仅支持ASCII。 - 数组和元组长度固定,确保内存布局可预测,与动态数组(
Vec
)形成互补。
示例:
let a: i32 = 42; // 有符号32位整数
let b: f64 = 3.14; // 64位浮点数
let c: bool = true; // 布尔值
let d: char = '😀'; // Unicode字符
let t: (i32, &str) = (10, "hello"); // 元组
let arr: [i32; 3] = [1, 2, 3]; // 数组
2. Rust中let
和let mut
的区别是什么?为什么默认不可变?
let
:用于声明不可变变量,一旦赋值后不能修改其值。let mut
:用于声明可变变量,允许后续修改其值。
默认不可变的原因:
Rust的设计理念强调内存安全和可预测性。默认不可变可减少意外修改导致的bug,尤其在多线程环境中能避免数据竞争。同时,不可变变量的内存布局更稳定,有助于编译器优化。
示例:
let x = 5;
// x = 6; // 编译错误:不能修改不可变变量
let mut y = 10;
y = 20; // 正确:可变变量可修改
3. 解释Rust的标量类型(Scalar Types),包括整数、浮点数、布尔值和字符的具体分类。
标量类型代表单个值,Rust的标量类型分类如下:
整数类型:
- 按位长分:8/16/32/64/128位,以及平台相关的
isize
(指针大小)和usize
(索引大小)。 - 按符号分:
i
(有符号,如i32
)和u
(无符号,如u64
)。 - 示例:
i8
(-128127)、`u32`(04294967295)。
- 按位长分:8/16/32/64/128位,以及平台相关的
浮点数类型:
f32
:32位单精度浮点数。f64
:64位双精度浮点数(默认浮点数类型)。- 示例:
let pi: f64 = 3.1415926;
。
布尔值(
bool
):- 仅两个可能值:
true
或false
,占1字节。 - 示例:
let is_ok: bool = 5 > 3;
。
- 仅两个可能值:
字符(
char
):- 占4字节,支持Unicode标量值(范围:
U+0000
`U+D7FF`和`U+E000`U+10FFFF
)。 - 可表示字母、数字、符号、emoji等。
- 示例:
let c: char = 'A'; let emoji: char = '🦀';
。
- 占4字节,支持Unicode标量值(范围:
4. 什么是复合类型(Compound Types)?举例说明元组(Tuple)和数组(Array)的用法及区别。
复合类型:将多个值组合成一个类型,Rust中主要有元组和数组。
元组(Tuple):
- 长度固定,元素类型可不同。
- 用法:用括号包裹逗号分隔的元素,可通过索引或解构访问。
- 示例:
let tuple: (i32, &str, f64) = (42, "hello", 3.14); println!("{}", tuple.0); // 访问第一个元素:42 // 解构元组 let (a, b, c) = tuple; println!("{} {} {}", a, b, c); // 42 hello 3.14
数组(Array):
- 长度固定,所有元素类型必须相同。
- 用法:用中括号包裹,格式为
[类型; 长度]
,通过索引访问。 - 示例:
let arr: [i32; 4] = [1, 2, 3, 4]; println!("{}", arr[2]); // 访问第三个元素:3 let filled_arr = [0; 5]; // 初始化5个0的数组:[0,0,0,0,0]
区别:
特性 | 元组(Tuple) | 数组(Array) |
---|---|---|
元素类型 | 可不同 | 必须相同 |
访问方式 | 索引(.0 )或解构 |
索引([0] ) |
长度灵活性 | 编译期固定,但元素类型灵活 | 编译期固定,类型单一 |
5. Rust中字符串有String
和&str
两种类型,它们的区别是什么?如何相互转换?
&str
:字符串切片,是对已分配字符串的不可变引用,存储在栈上,包含指向数据的指针和长度。通常用于引用字符串字面量(如"hello"
),无所有权。String
:动态字符串,存储在堆上,可增长或修改,拥有其数据的所有权。
区别:
类型 | 存储位置 | 可变性 | 所有权 | 典型用途 |
---|---|---|---|---|
&str |
栈(引用) | 不可变 | 无 | 引用字符串字面量或切片 |
String |
堆 | 可变 | 有 | 动态创建/修改字符串 |
相互转换:
String
→&str
:通过引用转换(&s
)或as_str()
方法。&str
→String
:通过to_string()
方法或String::from()
函数。
示例:
// &str 转 String
let s_str: &str = "hello";
let s_string1 = s_str.to_string();
let s_string2 = String::from(s_str);
// String 转 &str
let my_string = String::from("world");
let my_str1: &str = &my_string;
let my_str2 = my_string.as_str();
6. 解释Option<T>
枚举的作用,它解决了什么问题?与其他语言的null
有何不同?
Option<T>
枚举用于表示一个值可能存在(Some(T)
)或不存在(None
),定义如下:
enum Option<T> {
Some(T),
None,
}
作用:
- 显式标记可能为空的值,避免“空指针异常(NPE)”。
- 强制开发者在使用前处理“无值”的情况,确保类型安全。
解决的问题:
其他语言(如Java、C#)中的null
是一个特殊值,可赋值给任何引用类型,但访问null
会导致运行时错误。Option<T>
将“空”作为枚举的一个变体,编译期强制检查是否处理了None
,避免意外访问空值。
与null
的区别:
Option<T>
是类型系统的一部分,编译器确保对其值的访问必须先判断是否为Some
或None
。null
是运行时概念,编译器无法提前检测错误。
示例:
let some_number: Option<i32> = Some(5);
let no_number: Option<i32> = None;
// 必须处理None的情况(编译期检查)
match some_number {
Some(n) => println!("Number: {}", n),
None => println!("No number"),
}
7. Result<T, E>
枚举的用途是什么?如何处理其中的错误(使用match
、if let
或?
运算符)?
Result<T, E>
枚举用于表示操作可能成功(Ok(T)
)或失败(Err(E)
),定义如下:
enum Result<T, E> {
Ok(T), // 成功时包含的值
Err(E), // 失败时的错误信息
}
用途:
- 替代异常处理机制,显式处理错误,避免意外崩溃。
- 常用于文件操作、网络请求等可能失败的场景。
错误处理方式:
match
匹配:完整处理Ok
和Err
。let result: Result<i32, &str> = Ok(42); match result { Ok(val) => println!("Success: {}", val), Err(e) => println!("Error: {}", e), }
if let
简化匹配:仅处理一种情况(如只关心错误)。let result: Result<i32, &str> = Err("Failed"); if let Err(e) = result { println!("Error occurred: {}", e); }
?
运算符:快速传播错误(仅用于返回Result
的函数)。fn divide(a: i32, b: i32) -> Result<i32, String> { if b == 0 { return Err("Division by zero".to_string()); } Ok(a / b) } fn calculate() -> Result<i32, String> { let res = divide(10, 0)?; // 若错误则直接返回Err Ok(res) }
8. 什么是范围(Range)?..
和..=
有何区别?举例说明其在循环中的应用。
范围(Range) 是表示值序列的语法糖,常用于迭代。Rust中有两种范围:
start..end
:左闭右开范围,包含start
,不包含end
([start, end)
)。start..=end
:全包含范围,包含start
和end
([start, end]
)。
在循环中的应用:
范围常与for
循环配合,用于迭代数值序列。
示例:
// 1..5:包含1、2、3、4(不含5)
for i in 1..5 {
print!("{} ", i); // 输出:1 2 3 4
}
// 1..=5:包含1、2、3、4、5
for i in 1..=5 {
print!("{} ", i); // 输出:1 2 3 4 5
}
// 配合数组索引
let arr = [10, 20, 30, 40];
for i in 0..arr.len() {
println!("{}", arr[i]); // 遍历数组元素
}
9. Rust的注释有哪几种类型?文档注释(///
和//!
)的作用是什么?
Rust的注释类型包括:
单行注释:
//
,注释从//
到行尾。let x = 5; // 这是一个单行注释
多行注释:
/* ... */
,可跨多行。/* 这是一个 多行注释 */ let y = 10;
文档注释:用于生成API文档(通过
rustdoc
工具),分为两种:///
:为其下方的项(如函数、结构体)生成文档。//!
:为其所在的模块(mod
)生成文档。
文档注释示例:
//! 这是一个数学工具模块(模块级文档)
/// 计算两个数的和(函数级文档)
/// # 示例
/// ```
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
运行cargo doc --open
可生成HTML文档并查看。
10. 解释Rust的变量遮蔽(Shadowing),与mut
有何区别?
变量遮蔽(Shadowing):使用let
关键字声明与已存在变量同名的新变量,新变量会“遮蔽”旧变量,旧变量不再可访问。
特点:
- 新变量可与旧变量类型不同。
- 旧变量的生命周期在新变量声明时结束。
与mut
的区别:
特性 | 变量遮蔽(Shadowing) | mut (可变变量) |
---|---|---|
语法 | 使用let 重新声明同名变量 |
声明时加mut ,修改时直接赋值 |
类型 | 新变量可与旧变量类型不同 | 类型必须保持一致 |
可变性 | 旧变量仍不可变,只是被遮蔽 | 变量本身可修改值 |
示例:
// 变量遮蔽
let x = 5;
let x = x + 1; // 新x遮蔽旧x,类型仍为i32
let x = "now I'm a string"; // 新x类型为&str,遮蔽前一个x
// mut变量
let mut y = 5;
y = y + 1; // 正确:修改可变变量
// let y = "string"; // 若不加let则错误,因类型不匹配
遮蔽常用于临时转换变量类型或修改值但保持不可变语义的场景。