輸入輸出綁定 &'b mut self
在 Rust 中 Borrow Check 只保證記憶體安全,不保證執行邏輯符合需求
例如下面這個只能呼叫一次的 Iterator,就是把 return 的生命週期與 &mut self 綁一起
struct ByteIter<'a> {
remainder: &'a [u8],
}
impl<'a> ByteIter<'a> {
// 錯誤標註:將回傳值與 self 的 mutable borrow 綁定
fn next_restrictive<'b>(&'b mut self) -> Option<&'b u8> {
if self.remainder.is_empty() {
None
} else {
let byte = &self.remainder[0];
self.remainder = &self.remainder[1..];
Some(byte)
}
}
}
其中 &'b mut self -> Option<&'b u8> :
&'b mut self這叫做我會修改借用這個 struct,生命週期是'b- 回傳
&'b u8回傳的生命週期也得完全等於'b,Option<...>容納None
所以:
fn main() {
let mut bytes = ByteIter { remainder: b"123" };
let byte_1 = bytes.next_restrictive(); // 第一次借用:bytes 被標記為「正在被可變借用中」
// ERROR 編譯錯誤!
// 因為 byte_1 還活著,它要求 bytes 必須維持在「被 mutable 借用」的狀態。
// 你不能在同一時間進行第二次可變借用
let byte_2 = bytes.next_restrictive();
println!("{:?}, {:?}", byte_1, byte_2);
}
/*
cd examples\lifetime\semantics_a
cargo run --bin iter --release
error[E0499]: cannot borrow `bytes` as mutable more than once at a time
*/
而正確寫法應該是: fn next(&mut self) -> Option<&'remainder u8>
在這個狀態下,回傳了 byte_1 之後,借用就解除了,bytes 就可以再被借用
效能測試
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct
{
const unsigned char *remainder;
size_t len;
} ByteIter;
// Initialize Iterator
ByteIter new_iter(const unsigned char *data, size_t len)
{
ByteIter iter = {.remainder = data, .len = len};
return iter;
}
// Emulating Rust's Option<&u8>
// In C, returning NULL represents None
const unsigned char *next(ByteIter *self)
{
if (self->len == 0)
{
return NULL;
}
const unsigned char *byte = &self->remainder[0];
self->remainder++;
self->len--;
return byte;
}
int main()
{
size_t data_size = 100000000; // 100 million bytes
unsigned char *data = malloc(data_size);
if (!data)
return 1;
for (size_t i = 0; i < data_size; i++)
{
data[i] = 1;
}
ByteIter iter = new_iter(data, data_size);
unsigned long long sum = 0;
printf("C: Calculation started...\n");
clock_t start = clock();
while (1)
{
const unsigned char *b = next(&iter);
if (!b)
break;
sum += *b;
}
clock_t end = clock();
double cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("C Sum: %llu\n", sum);
printf("C Time elapsed: %.3f seconds\n", cpu_time_used);
free(data);
return 0;
}
/*
cd examples\lifetime\semantics_a
gcc test.c -O3 -march=native -o test
./test
C: Calculation started...
C Sum: 100000000
C Time elapsed: 0.017 seconds
*/
use std::time::Instant;
struct ByteIter<'remainder> {
remainder: &'remainder [u8],
}
impl<'remainder> ByteIter<'remainder> {
fn new(remainder: &'remainder [u8]) -> Self {
Self { remainder }
}
// Correct lifetime: bound to the data source 'remainder
fn next(&mut self) -> Option<&'remainder u8> {
if self.remainder.is_empty() {
None
} else {
let byte = &self.remainder[0];
self.remainder = &self.remainder[1..];
Some(byte)
}
}
}
fn main() {
// Prepare 100 million bytes (approx. 100MB)
let data = vec![1u8; 100_000_000];
let mut iter = ByteIter::new(&data);
let mut sum: u64 = 0;
println!("Rust: Calculation started...");
let start = Instant::now();
// The Option<T> is optimized away by the compiler into a null pointer check
while let Some(b) = iter.next() {
sum += *b as u64;
}
let duration = start.elapsed();
println!("Rust Sum: {}", sum);
println!("Rust Time elapsed: {:.3?}", duration);
}
/*
cd examples/lifetime/semantics_a
cargo run --bin test --release
Rust: Calculation started...
Rust Sum: 100000000
Rust Time elapsed: 14.645ms
*/
效能幾乎一樣