如何在实现特征时明确指定生命周期?
How can I explicitly specify a lifetime when implementing a trait?
鉴于下面的实现,基本上我有一些可以通过 i32 id 字段或字符串字段查找的项目集合。为了能够互换使用,使用了特征 "IntoKey",并且 match
分派到适当的查找映射;这一切都适用于我在 MapCollection
impl:
中定义的 get
use std::collections::HashMap;
use std::ops::Index;
enum Key<'a> {
I32Key(&'a i32),
StringKey(&'a String),
}
trait IntoKey<'a> {
fn into_key(&'a self) -> Key<'a>;
}
impl<'a> IntoKey<'a> for i32 {
fn into_key(&'a self) -> Key<'a> { Key::I32Key(self) }
}
impl<'a> IntoKey<'a> for String {
fn into_key(&'a self) -> Key<'a> { Key::StringKey(self) }
}
#[derive(Debug)]
struct Bar {
i: i32,
n: String,
}
struct MapCollection
{
items: Vec<Bar>,
id_map: HashMap<i32, usize>,
name_map: HashMap<String, usize>,
}
impl MapCollection {
fn new(items: Vec<Bar>) -> MapCollection {
let mut is = HashMap::new();
let mut ns = HashMap::new();
for (idx, item) in items.iter().enumerate() {
is.insert(item.i, idx);
ns.insert(item.n.clone(), idx);
}
MapCollection {
items: items,
id_map: is,
name_map: ns,
}
}
fn get<'a, K>(&self, key: &'a K) -> Option<&Bar>
where K: IntoKey<'a> //'
{
match key.into_key() {
Key::I32Key(i) => self.id_map.get(i).and_then(|idx| self.items.get(*idx)),
Key::StringKey(s) => self.name_map.get(s).and_then(|idx| self.items.get(*idx)),
}
}
}
fn main() {
let bars = vec![Bar { i:1, n:"foo".to_string() }, Bar { i:2, n:"far".to_string() }];
let map = MapCollection::new(bars);
if let Some(bar) = map.get(&1) {
println!("{:?}", bar);
}
if map.get(&3).is_none() {
println!("no item numbered 3");
}
if let Some(bar) = map.get(&"far".to_string()) {
println!("{:?}", bar);
}
if map.get(&"baz".to_string()).is_none() {
println!("no item named baz");
}
}
但是,如果我想为此结构实现 std::ops::Index
,如果我尝试执行以下操作:
impl<'a, K> Index<K> for MapCollection
where K: IntoKey<'a> {
type Output = Bar;
fn index<'b>(&'b self, k: &K) -> &'b Bar {
self.get(k).expect("no element")
}
}
我遇到编译器错误:
src/main.rs:70:18: 70:19 error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
src/main.rs:70 self.get(k).expect("no element")
^
src/main.rs:69:5: 71:6 help: consider using an explicit lifetime parameter as shown: fn index<'b>(&'b self, k: &'a K) -> &'b Bar
src/main.rs:69 fn index<'b>(&'b self, k: &K) -> &'b Bar {
src/main.rs:70 self.get(k).expect("no element")
src/main.rs:71 }
我找不到在这里指定不同生命周期的方法;遵循编译器的建议是不允许的,因为它更改了函数签名并且不再匹配特征,而且我尝试的任何其他方法都不满足生命周期规范。
我知道我可以分别为每个案例(i32、字符串)实现特征,而不是尝试为 IntoKey 实现一次,但我更普遍地试图了解生命周期和适当的用法。本质上:
- 编译器是否真的阻止了问题?这种方法有什么不妥当的地方吗?
- 我是否错误地指定了我的生命周期?对我来说,Key/IntoKey 中的生命周期
'a
表明引用只需要足够长的时间来进行查找;与 index
fn 相关联的生命周期 'b
表明查找产生的引用将在包含 MapCollection
. 时存在
- 或者我只是没有使用正确的语法来指定所需的信息?
(使用 rustc 1.0.0-nightly (b63cee4a1 2015-02-14 17:01:11 +0000)
)
您是否打算在要存储生命周期 'a
的引用的结构上实现 IntoKey
?如果没有,您可以将特征及其实现更改为:
trait IntoKey {
fn into_key<'a>(&'a self) -> Key<'a>;
}
这是一般推荐的定义风格,如果你会用的话。如果你不能...
让我们看看这个较小的复制品:
use std::collections::HashMap;
use std::ops::Index;
struct Key<'a>(&'a u8);
trait IntoKey<'a> { //'
fn into_key(&'a self) -> Key<'a>;
}
struct MapCollection;
impl MapCollection {
fn get<'a, K>(&self, key: &'a K) -> &u8
where K: IntoKey<'a> //'
{
unimplemented!()
}
}
impl<'a, K> Index<K> for MapCollection //'
where K: IntoKey<'a> //'
{
type Output = u8;
fn index<'b>(&'b self, k: &K) -> &'b u8 { //'
self.get(k)
}
}
fn main() {
}
问题出在get
:
fn get<'a, K>(&self, key: &'a K) -> &u8
where K: IntoKey<'a>
在这里,我们参考了 K
,它必须 存活到 我们从中得到的 Key
。然而,索引特征并不能保证:
fn index<'b>(&'b self, k: &K) -> &'b u8
你可以通过简单地给 key
一个新的生命周期来解决这个问题:
fn get<'a, 'b, K>(&self, key: &'b K) -> &u8
where K: IntoKey<'a>
或更简洁:
fn get<'a, K>(&self, key: &K) -> &u8
where K: IntoKey<'a>
鉴于下面的实现,基本上我有一些可以通过 i32 id 字段或字符串字段查找的项目集合。为了能够互换使用,使用了特征 "IntoKey",并且 match
分派到适当的查找映射;这一切都适用于我在 MapCollection
impl:
get
use std::collections::HashMap;
use std::ops::Index;
enum Key<'a> {
I32Key(&'a i32),
StringKey(&'a String),
}
trait IntoKey<'a> {
fn into_key(&'a self) -> Key<'a>;
}
impl<'a> IntoKey<'a> for i32 {
fn into_key(&'a self) -> Key<'a> { Key::I32Key(self) }
}
impl<'a> IntoKey<'a> for String {
fn into_key(&'a self) -> Key<'a> { Key::StringKey(self) }
}
#[derive(Debug)]
struct Bar {
i: i32,
n: String,
}
struct MapCollection
{
items: Vec<Bar>,
id_map: HashMap<i32, usize>,
name_map: HashMap<String, usize>,
}
impl MapCollection {
fn new(items: Vec<Bar>) -> MapCollection {
let mut is = HashMap::new();
let mut ns = HashMap::new();
for (idx, item) in items.iter().enumerate() {
is.insert(item.i, idx);
ns.insert(item.n.clone(), idx);
}
MapCollection {
items: items,
id_map: is,
name_map: ns,
}
}
fn get<'a, K>(&self, key: &'a K) -> Option<&Bar>
where K: IntoKey<'a> //'
{
match key.into_key() {
Key::I32Key(i) => self.id_map.get(i).and_then(|idx| self.items.get(*idx)),
Key::StringKey(s) => self.name_map.get(s).and_then(|idx| self.items.get(*idx)),
}
}
}
fn main() {
let bars = vec![Bar { i:1, n:"foo".to_string() }, Bar { i:2, n:"far".to_string() }];
let map = MapCollection::new(bars);
if let Some(bar) = map.get(&1) {
println!("{:?}", bar);
}
if map.get(&3).is_none() {
println!("no item numbered 3");
}
if let Some(bar) = map.get(&"far".to_string()) {
println!("{:?}", bar);
}
if map.get(&"baz".to_string()).is_none() {
println!("no item named baz");
}
}
但是,如果我想为此结构实现 std::ops::Index
,如果我尝试执行以下操作:
impl<'a, K> Index<K> for MapCollection
where K: IntoKey<'a> {
type Output = Bar;
fn index<'b>(&'b self, k: &K) -> &'b Bar {
self.get(k).expect("no element")
}
}
我遇到编译器错误:
src/main.rs:70:18: 70:19 error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
src/main.rs:70 self.get(k).expect("no element")
^
src/main.rs:69:5: 71:6 help: consider using an explicit lifetime parameter as shown: fn index<'b>(&'b self, k: &'a K) -> &'b Bar
src/main.rs:69 fn index<'b>(&'b self, k: &K) -> &'b Bar {
src/main.rs:70 self.get(k).expect("no element")
src/main.rs:71 }
我找不到在这里指定不同生命周期的方法;遵循编译器的建议是不允许的,因为它更改了函数签名并且不再匹配特征,而且我尝试的任何其他方法都不满足生命周期规范。
我知道我可以分别为每个案例(i32、字符串)实现特征,而不是尝试为 IntoKey 实现一次,但我更普遍地试图了解生命周期和适当的用法。本质上:
- 编译器是否真的阻止了问题?这种方法有什么不妥当的地方吗?
- 我是否错误地指定了我的生命周期?对我来说,Key/IntoKey 中的生命周期
'a
表明引用只需要足够长的时间来进行查找;与index
fn 相关联的生命周期'b
表明查找产生的引用将在包含MapCollection
. 时存在
- 或者我只是没有使用正确的语法来指定所需的信息?
(使用 rustc 1.0.0-nightly (b63cee4a1 2015-02-14 17:01:11 +0000)
)
您是否打算在要存储生命周期 'a
的引用的结构上实现 IntoKey
?如果没有,您可以将特征及其实现更改为:
trait IntoKey {
fn into_key<'a>(&'a self) -> Key<'a>;
}
这是一般推荐的定义风格,如果你会用的话。如果你不能...
让我们看看这个较小的复制品:
use std::collections::HashMap;
use std::ops::Index;
struct Key<'a>(&'a u8);
trait IntoKey<'a> { //'
fn into_key(&'a self) -> Key<'a>;
}
struct MapCollection;
impl MapCollection {
fn get<'a, K>(&self, key: &'a K) -> &u8
where K: IntoKey<'a> //'
{
unimplemented!()
}
}
impl<'a, K> Index<K> for MapCollection //'
where K: IntoKey<'a> //'
{
type Output = u8;
fn index<'b>(&'b self, k: &K) -> &'b u8 { //'
self.get(k)
}
}
fn main() {
}
问题出在get
:
fn get<'a, K>(&self, key: &'a K) -> &u8
where K: IntoKey<'a>
在这里,我们参考了 K
,它必须 存活到 我们从中得到的 Key
。然而,索引特征并不能保证:
fn index<'b>(&'b self, k: &K) -> &'b u8
你可以通过简单地给 key
一个新的生命周期来解决这个问题:
fn get<'a, 'b, K>(&self, key: &'b K) -> &u8
where K: IntoKey<'a>
或更简洁:
fn get<'a, K>(&self, key: &K) -> &u8
where K: IntoKey<'a>