如何通过Rust面试:顶级高级问题
Rust——最受欢迎的系统编程语言之一,对高级开发者的需求持续增长。如果你已经掌握了基本语法和所有权系统,面试中等待你的将是考察对unsafe代码、异步、复杂生命周期和元编程深入理解的问题。在本文中,我们将分析关键主题并提供代码示例,帮助你自信地通过面试。
1. Unsafe Rust:何时以及为何使用?
高级面试中最常见的问题之一:“解释一下你何时会使用unsafe,以及这会违反哪些保证”。
答案应包括unsafe提供的四个主要能力:
- 解引用裸指针(
*const T和*mut T) - 调用unsafe函数(例如FFI)
- 访问可变静态变量
- 实现unsafe trait
示例:实现一个手动管理内存的简单动态数组。
use std::alloc::{alloc, dealloc, Layout};use std::ptr;
struct MyVec { ptr: *mut i32, len: usize, cap: usize,}
impl MyVec { fn new() -> Self { MyVec { ptr: ptr::null_mut(), len: 0, cap: 0, } }
fn push(&mut self, val: i32) { if self.len == self.cap { let new_cap = if self.cap == 0 { 1 } else { self.cap * 2 }; let new_layout = Layout::array::<i32>(new_cap).unwrap(); let new_ptr = unsafe { alloc(new_layout) as *mut i32 }; // 复制旧数据 unsafe { ptr::copy_nonoverlapping(self.ptr, new_ptr, self.len); if !self.ptr.is_null() { let old_layout = Layout::array::<i32>(self.cap).unwrap(); dealloc(self.ptr as *mut u8, old_layout); } } self.ptr = new_ptr; self.cap = new_cap; } unsafe { ptr::write(self.ptr.add(self.len), val); } self.len += 1; }}
impl Drop for MyVec { fn drop(&mut self) { if !self.ptr.is_null() { unsafe { let layout = Layout::array::<i32>(self.cap).unwrap(); dealloc(self.ptr as *mut u8, layout); } } }}建议:始终强调unsafe不是禁用检查,而是承担起维护不变量的责任。
2. 生命周期与变型:编译器如何检查引用
在高级水平,你会被要求解释什么是变型(协变、逆变、不变)在生命周期上下文中的含义。这是理解某些类型无法编译的关键。
- 协变:
&'a T——如果'a大于'b,则&'a T可以用作&'b T。 - 逆变:
fn(T)——如果'a大于'b,则fn(&'b T)可以用作fn(&'a T)。 - 不变:
&mut T——生命周期必须完全匹配。
示例:为什么以下代码无法编译?
fn foo<'a>(x: &'a mut &'a str, y: &'a str) { *x = y;}
fn main() { let mut s = "hello"; let r = &s; foo(&mut s, r); // 错误!}答案:&mut T在T上是不变的,因此内部引用的生命周期必须与外部引用的生命周期完全一致。编译器无法“压缩”r的生命周期。