Rust 借用與引用實戰
引言
借用是 Rust 中使用值而不獲取其所有權的方式。通過引用,我們可以在不轉移所有權的情況下訪問數據。Rust 的借用檢查器確保引用始終有效。
借用規則
借用遵循兩條關鍵規則:
- 在任意給定時間,要麼只能有一個可變引用,要麼只能有多個不可變引用
- 引用必須總是有效的
基礎示例
fn main() {
let mut s = String::from("hello");
// 不可變借用
let r1 = &s;
let r2 = &s;
println!("{} and {}", r1, r2);
// 可變借用
let r3 = &mut s;
r3.push_str(", world");
println!("{}", r3);
}
複雜案例:實現一個緩存系統
下面實現一個支持併發訪問的緩存系統,展示借用在實際場景中的應用。
use std::collections::HashMap;
use std::hash::Hash;
struct Cache<K, V>
where
K: Eq + Hash + Clone,
V: Clone,
{
store: HashMap<K, V>,
max_size: usize,
access_count: HashMap<K, usize>,
}
impl<K, V> Cache<K, V>
where
K: Eq + Hash + Clone,
V: Clone,
{
fn new(max_size: usize) -> Self {
Cache {
store: HashMap::new(),
max_size,
access_count: HashMap::new(),
}
}
// 不可變借用:讀取緩存
fn get(&mut self, key: &K) -> Option<&V> {
if let Some(count) = self.access_count.get_mut(key) {
*count += 1;
}
self.store.get(key)
}
// 可變借用:插入或更新緩存
fn insert(&mut self, key: K, value: V) {
if self.store.len() >= self.max_size && !self.store.contains_key(&key) {
self.evict_least_used();
}
self.store.insert(key.clone(), value);
self.access_count.insert(key, 1);
}
// 淘汰最少使用的項
fn evict_least_used(&mut self) {
if let Some((&ref key, _)) = self.access_count
.iter()
.min_by_key(|(_, &count)| count)
{
let key_to_remove = key.clone();
self.store.remove(&key_to_remove);
self.access_count.remove(&key_to_remove);
}
}
// 不可變借用:獲取統計信息
fn stats(&self) -> CacheStats {
let total_accesses: usize = self.access_count.values().sum();
CacheStats {
size: self.store.len(),
max_size: self.max_size,
total_accesses,
}
}
// 不可變借用:檢查是否包含鍵
fn contains(&self, key: &K) -> bool {
self.store.contains_key(key)
}
// 可變借用:清空緩存
fn clear(&mut self) {
self.store.clear();
self.access_count.clear();
}
}
struct CacheStats {
size: usize,
max_size: usize,
total_accesses: usize,
}
impl CacheStats {
fn print(&self) {
println!("緩存統計:");
println!(" 當前大小: {}/{}", self.size, self.max_size);
println!(" 總訪問次數: {}", self.total_accesses);
println!(" 使用率: {:.2}%",
(self.size as f64 / self.max_size as f64) * 100.0);
}
}
fn demonstrate_cache() {
let mut cache = Cache::new(3);
// 插入數據
cache.insert("user:1", "Alice");
cache.insert("user:2", "Bob");
cache.insert("user:3", "Charlie");
// 讀取數據(不可變借用)
if let Some(user) = cache.get(&"user:1") {
println!("找到用户: {}", user);
}
// 再次訪問增加計數
cache.get(&"user:1");
cache.get(&"user:2");
// 插入新數據,觸發淘汰
cache.insert("user:4", "David");
// 檢查緩存內容
println!("user:3 還在緩存中嗎? {}", cache.contains(&"user:3"));
// 打印統計信息
cache.stats().print();
}
// 演示引用的生命週期
fn reference_lifetime_demo() {
let data = vec![1, 2, 3, 4, 5];
// 借用切片
let slice = &data[1..4];
println!("切片內容: {:?}", slice);
// 同時存在多個不可變引用
let ref1 = &data;
let ref2 = &data;
let ref3 = &data;
println!("Sum via ref1: {}", ref1.iter().sum::<i32>());
println!("Len via ref2: {}", ref2.len());
println!("First via ref3: {}", ref3[0]);
}
// 實現一個需要精確控制借用的數據結構
struct BorrowTracker<T> {
data: T,
borrow_count: usize,
mut_borrowed: bool,
}
impl<T> BorrowTracker<T> {
fn new(data: T) -> Self {
BorrowTracker {
data,
borrow_count: 0,
mut_borrowed: false,
}
}
fn borrow(&mut self) -> Result<&T, String> {
if self.mut_borrowed {
return Err("數據已被可變借用".to_string());
}
self.borrow_count += 1;
Ok(&self.data)
}
fn borrow_mut(&mut self) -> Result<&mut T, String> {
if self.borrow_count > 0 {
return Err(format!("數據已被借用 {} 次", self.borrow_count));
}
if self.mut_borrowed {
return Err("數據已被可變借用".to_string());
}
self.mut_borrowed = true;
Ok(&mut self.data)
}
fn release(&mut self) {
if self.borrow_count > 0 {
self.borrow_count -= 1;
}
}
fn release_mut(&mut self) {
self.mut_borrowed = false;
}
}
fn demonstrate_borrow_tracking() {
let mut tracker = BorrowTracker::new(vec![1, 2, 3]);
// 不可變借用
match tracker.borrow() {
Ok(data) => println!("借用成功: {:?}", data),
Err(e) => println!("借用失敗: {}", e),
}
tracker.release();
// 可變借用
match tracker.borrow_mut() {
Ok(data) => {
data.push(4);
println!("可變借用成功,修改後: {:?}", data);
},
Err(e) => println!("可變借用失敗: {}", e),
}
tracker.release_mut();
}
fn main() {
demonstrate_cache();
println!("\n---\n");
reference_lifetime_demo();
println!("\n---\n");
demonstrate_borrow_tracking();
}
借用與迭代器
迭代器是借用的典型應用場景:
fn iterator_borrowing() {
let numbers = vec![1, 2, 3, 4, 5];
// iter() 創建不可變引用的迭代器
let sum: i32 = numbers.iter().sum();
println!("和: {}", sum);
// 原始數據仍然可用
println!("原始數據: {:?}", numbers);
// 過濾和映射
let even_squares: Vec<i32> = numbers
.iter()
.filter(|&&x| x % 2 == 0)
.map(|&x| x * x)
.collect();
println!("偶數的平方: {:?}", even_squares);
}
總結
借用和引用是 Rust 實現內存安全的關鍵機制。通過編譯時檢查,Rust 確保引用的正確使用,避免數據競爭和懸垂引用。
本文章為轉載內容,我們尊重原作者對文章享有的著作權。如有內容錯誤或侵權問題,歡迎原作者聯繫我們進行內容更正或刪除文章。