我如何摆脱这个 "argument requires that borrow lasts for `'1`" 错误?
How do I get rid of this "argument requires that borrow lasts for `'1`" error?
我正在编写我的第一个 Rust 程序,实现一个简单的 LruCache 并处理一些事件。
LruCache 在闭包中使用,以跟踪轮询后已处理的事件,如果已处理则跳过它们:
let mut evt_cache = LruSetCache::new(100_000);
// poll for events
loop {
let unprocessed_events: Vec<Event> = unprocessed_events_serialized
.into_iter()
.filter_map(|evt| {
let key = evt.as_bytes();
if evt_cache.exists(&key) {
None
} else {
evt_cache.put(key.clone());
Some(Event::from_bytes(&evt.as_bytes()).unwrap())
}
})
.collect();
// ... process events
}
但是,编译器对我将 [u8]
键传递给缓存的方式不满意:
--> src/main.rs:58:27
|
51 | let mut evt_cache = LruSetCache::new(100_000);
| ------------ lifetime `'1` appears in the type of `evt_cache`
...
58 | let key = evt.as_bytes();
| ^^^^^^^^^^^^^ borrowed value does not live long enough
...
63 | evt_cache.put(key);
| ----------------- argument requires that `evt` is borrowed for `'1`
...
66 | })
| - `evt` dropped here while still borrowed
LruSetCache 可能是罪魁祸首:
use std::collections::{HashSet, VecDeque};
use std::hash::Hash;
/// A LruCache that stores values (instead of (key, values) pairs).
///
/// You can
/// - insert elements,
/// - check for existence of an item.
/// It automatically frees memory after max_capacity is reached.
///
/// Basically, an LRUCache Backed by an `HashSet` instead of an `HashMap` that saves (little) memory and (very few) cpu cycles.
pub struct LruSetCache<T> {
/// Elements will start to be evicted from the cache when this number of elements is reached.
///
/// Note: In theory, we could avoid having this field here and keep track of the capacity by
/// initializing the underlying `items` with `HashSet::with_capacity()` and by querying
/// `HashSet::capacity()` when needed. However, if this inner detail implementation of `HashSet`
/// that grows the capacity and starts re-allocating when the element is close to being full
/// (rather than being full), then the LruCache would break.
capacity: usize,
/// Tracks which elements were added first, because they'll need to be removed when the
/// maximum capacity is reached.
/// Keeps track of the current capacity (`items.len()`).
items: VecDeque<T>,
/// Allows fast item existence check (O(1) as opposed to O(n) with the above VecDeque).
items_set: HashSet<T>,
}
impl<T> LruCache<T> {
pub fn new(capacity: usize) -> LruCache<T> {
LruCache {
capacity,
items: VecDeque::with_capacity(capacity),
items_set: HashSet::with_capacity(capacity),
}
}
/// Checks whether an item exists.
pub fn exists(&self, item: &T) -> bool {
self.items_set.get(item).is_some()
}
/// Inserts an item.
pub fn put(&mut self, item: T) {
if self.items.len() >= self.capacity {
// evict item that was inserted least recently
self.items.pop_back();
self.items_set.remove(&item);
}
self.items.push_front(&item);
self.items_set.insert(&item);
}
}
到目前为止我的调试之旅
我通过指定 T
参数而不是 LruSetCache::set
中的 &T
将所有权传递给 LruCache::put
函数,但这似乎还不够Rust 让它在闭包结束后继续存在。据我了解,move
只会创建另一个指向原始值的指针,所以我明白为什么这还不够。
所以我在传入之前尝试了 .clone()
ing 字符串。我知道错误是 [u8]
键在闭包结束时内存不足。但是,重新克隆并传递它并没有帮助。
我猜这是因为它是在堆栈中克隆的,而不是在可以逃避函数清理的堆中。
所以我已经尝试 Box
ing 键,所以它会存在于堆中而不是堆栈中,并且在函数 returns 时不会被清除。但我得到的错误信息基本相同:
let key = Box::new(evt.as_bytes());
53 | let mut evt_cache = LruSetCache::new(100_000);
| ------------- lifetime `'1` appears in the type of `evt_cache`
...
60 | let key = Box::new(evt.as_bytes());
| ^^^^^^^^^^^^^^ borrowed value does not live long enough
...
65 | evt_cache.put(key);
| ------------------ argument requires that `eat` is borrowed for `'1`
...
68 | })
| - `evt` dropped here while still borrowed
Rust 出于某种原因希望 evt
比闭包更有效,但我只需要密钥即可。
这是怎么回事?
尝试用 .to_owned()
或 .to_vec()
替换 .clone()
。
let unprocessed_events: Vec<Event> = unprocessed_events_serialized
.into_iter()
.filter_map(|evt| {
let key = evt.as_bytes().to_owned(); // add `.to_vec()` or `.to_owned()`
if evt_cache.exists(&key) {
None
} else {
evt_cache.put(key); // Can remove .clone()
Some(Event::from_bytes(&evt.as_bytes()).unwrap())
}
})
.collect();
这将克隆整个密钥,因此如果您的密钥非常大,可能不适合。
以上版本将始终 克隆整个密钥,即使它已经存在于evt_cache 中。如果 .exists()
函数在 Borrow<T>
上是通用的,而不是仅仅接受引用,则可以(可能)解除此限制。 (查看 HashSet::contains
是如何定义的 https://doc.rust-lang.org/std/collections/struct.HashSet.html#method.contains)。这样你只需要在 evt_cache.put
.
上调用 .to_owned()
Shouldn't .clone()
and .to_vec()
do the same thing here?
克隆特性的定义方式使我们无法生成与传入的类型不同的类型。这会导致 &[T]
、&str
、[= 等类型出现问题23=] 等,通过附加的 ToOwned
特性解决。
来自 https://doc.rust-lang.org/std/borrow/trait.ToOwned.html
A generalization of Clone to borrowed data.
Some types make it possible to go from borrowed to owned, usually by implementing the Clone trait. But Clone works only for going from &T to T. The ToOwned trait generalizes Clone to construct owned data from any borrow of a given type.
因此 &[u8]
的 .to_owned()
确实如您所愿,它只是调用 .to_vec()
我正在编写我的第一个 Rust 程序,实现一个简单的 LruCache 并处理一些事件。
LruCache 在闭包中使用,以跟踪轮询后已处理的事件,如果已处理则跳过它们:
let mut evt_cache = LruSetCache::new(100_000);
// poll for events
loop {
let unprocessed_events: Vec<Event> = unprocessed_events_serialized
.into_iter()
.filter_map(|evt| {
let key = evt.as_bytes();
if evt_cache.exists(&key) {
None
} else {
evt_cache.put(key.clone());
Some(Event::from_bytes(&evt.as_bytes()).unwrap())
}
})
.collect();
// ... process events
}
但是,编译器对我将 [u8]
键传递给缓存的方式不满意:
--> src/main.rs:58:27
|
51 | let mut evt_cache = LruSetCache::new(100_000);
| ------------ lifetime `'1` appears in the type of `evt_cache`
...
58 | let key = evt.as_bytes();
| ^^^^^^^^^^^^^ borrowed value does not live long enough
...
63 | evt_cache.put(key);
| ----------------- argument requires that `evt` is borrowed for `'1`
...
66 | })
| - `evt` dropped here while still borrowed
LruSetCache 可能是罪魁祸首:
use std::collections::{HashSet, VecDeque};
use std::hash::Hash;
/// A LruCache that stores values (instead of (key, values) pairs).
///
/// You can
/// - insert elements,
/// - check for existence of an item.
/// It automatically frees memory after max_capacity is reached.
///
/// Basically, an LRUCache Backed by an `HashSet` instead of an `HashMap` that saves (little) memory and (very few) cpu cycles.
pub struct LruSetCache<T> {
/// Elements will start to be evicted from the cache when this number of elements is reached.
///
/// Note: In theory, we could avoid having this field here and keep track of the capacity by
/// initializing the underlying `items` with `HashSet::with_capacity()` and by querying
/// `HashSet::capacity()` when needed. However, if this inner detail implementation of `HashSet`
/// that grows the capacity and starts re-allocating when the element is close to being full
/// (rather than being full), then the LruCache would break.
capacity: usize,
/// Tracks which elements were added first, because they'll need to be removed when the
/// maximum capacity is reached.
/// Keeps track of the current capacity (`items.len()`).
items: VecDeque<T>,
/// Allows fast item existence check (O(1) as opposed to O(n) with the above VecDeque).
items_set: HashSet<T>,
}
impl<T> LruCache<T> {
pub fn new(capacity: usize) -> LruCache<T> {
LruCache {
capacity,
items: VecDeque::with_capacity(capacity),
items_set: HashSet::with_capacity(capacity),
}
}
/// Checks whether an item exists.
pub fn exists(&self, item: &T) -> bool {
self.items_set.get(item).is_some()
}
/// Inserts an item.
pub fn put(&mut self, item: T) {
if self.items.len() >= self.capacity {
// evict item that was inserted least recently
self.items.pop_back();
self.items_set.remove(&item);
}
self.items.push_front(&item);
self.items_set.insert(&item);
}
}
到目前为止我的调试之旅
我通过指定 T
参数而不是 LruSetCache::set
中的 &T
将所有权传递给 LruCache::put
函数,但这似乎还不够Rust 让它在闭包结束后继续存在。据我了解,move
只会创建另一个指向原始值的指针,所以我明白为什么这还不够。
所以我在传入之前尝试了 .clone()
ing 字符串。我知道错误是 [u8]
键在闭包结束时内存不足。但是,重新克隆并传递它并没有帮助。
我猜这是因为它是在堆栈中克隆的,而不是在可以逃避函数清理的堆中。
所以我已经尝试 Box
ing 键,所以它会存在于堆中而不是堆栈中,并且在函数 returns 时不会被清除。但我得到的错误信息基本相同:
let key = Box::new(evt.as_bytes());
53 | let mut evt_cache = LruSetCache::new(100_000);
| ------------- lifetime `'1` appears in the type of `evt_cache`
...
60 | let key = Box::new(evt.as_bytes());
| ^^^^^^^^^^^^^^ borrowed value does not live long enough
...
65 | evt_cache.put(key);
| ------------------ argument requires that `eat` is borrowed for `'1`
...
68 | })
| - `evt` dropped here while still borrowed
Rust 出于某种原因希望 evt
比闭包更有效,但我只需要密钥即可。
这是怎么回事?
尝试用 .to_owned()
或 .to_vec()
替换 .clone()
。
let unprocessed_events: Vec<Event> = unprocessed_events_serialized
.into_iter()
.filter_map(|evt| {
let key = evt.as_bytes().to_owned(); // add `.to_vec()` or `.to_owned()`
if evt_cache.exists(&key) {
None
} else {
evt_cache.put(key); // Can remove .clone()
Some(Event::from_bytes(&evt.as_bytes()).unwrap())
}
})
.collect();
这将克隆整个密钥,因此如果您的密钥非常大,可能不适合。
以上版本将始终 克隆整个密钥,即使它已经存在于evt_cache 中。如果 .exists()
函数在 Borrow<T>
上是通用的,而不是仅仅接受引用,则可以(可能)解除此限制。 (查看 HashSet::contains
是如何定义的 https://doc.rust-lang.org/std/collections/struct.HashSet.html#method.contains)。这样你只需要在 evt_cache.put
.
.to_owned()
Shouldn't
.clone()
and.to_vec()
do the same thing here?
克隆特性的定义方式使我们无法生成与传入的类型不同的类型。这会导致 &[T]
、&str
、[= 等类型出现问题23=] 等,通过附加的 ToOwned
特性解决。
来自 https://doc.rust-lang.org/std/borrow/trait.ToOwned.html
A generalization of Clone to borrowed data.
Some types make it possible to go from borrowed to owned, usually by implementing the Clone trait. But Clone works only for going from &T to T. The ToOwned trait generalizes Clone to construct owned data from any borrow of a given type.
因此 &[u8]
的 .to_owned()
确实如您所愿,它只是调用 .to_vec()