Using a generic trait as a trait parameter - rust

How can I use related generic types? Here's what I've got (only the first line is giving me trouble):
impl<G, GS> TreeNode<GS> where G: Game, GS: GameState<G>{
pub fn expand(&mut self, game: &G){
if !self.expanded{
let child_states = self.data.generate_children(game);
for state in child_states{
self.add_child_with_value(state);
}
}
}
}
GameState is a trait that is generic to a Game, and self.data implements GameState<Game> of this type. The compiler tells me
error[E0207]: the type parameter `G` is not constrained by the impl trait, self type, or predicates
--> src/mcts.rs:42:6
|
42 | impl<G, GS> TreeNode<GS> where G: Game, GS: GameState<G>{
| ^ unconstrained type parameter
error: aborting due to previous error
but it seems to me like I'm constraining G both in the expand function, and in the fact that G needs to belong to GS.
Here are some more definitions as of now:
trait GameState<G: Game>: std::marker::Sized + Debug{
fn generate_children(&self, game: &G) -> Vec<Self>;
fn get_initial_state(game: &G) -> Self;
}
trait Game{}
struct TreeNode<S> where S: Sized{
parent: *mut TreeNode<S>,
expanded: bool,
pub children: Vec<TreeNode<S>>,
pub data: S,
pub n: u32
}
impl<S> TreeNode<S>{
pub fn new(data: S) -> Self{
TreeNode {
parent: null_mut(),
expanded: false,
children: vec![],
data,
n: 0
}
}
pub fn add_child(&mut self, mut node: TreeNode<S>){
node.parent = self;
self.children.push(node);
}
pub fn add_child_with_value(&mut self, val: S){
let new_node = TreeNode::new(val);
self.add_child(new_node);
}
pub fn parent(&self) -> &Self{
unsafe{
&*self.parent
}
}
}

impl<G, GS> TreeNode<GS> where G: Game, GS: GameState<G>{
// ...
}
The problem is that G is not constrained, so there may be multiple (possibly conflicting) implementations in this block, since GS maybe implement GameState<G> for multiple G. The parameter G is ambiguous.
If you want to keep GameState<G> able to be implemented for multiple G, you should move the constraints from the impl block to the method instead:
// note: G is now a type parameter of the method, not the impl block, which is fine
impl<GS> TreeNode<GS> {
pub fn expand<G>(&mut self, game: &G) where G: Game, GS: GameState<G> {
if !self.expanded{
let child_states = self.data.generate_children(game);
for state in child_states{
self.add_child_with_value(state);
}
}
}
}
If you only want GameState to be implemented for a single G, you should make G an associated type of GameState instead of a generic type parameter:
trait GameState: std::marker::Sized + Debug {
type G: Game;
fn generate_children(&self, game: &Self::G) -> Vec<Self>;
fn get_initial_state(game: &Self::G) -> Self;
}
// note: now G is given by the GameState implementation instead of
// being a free type parameter
impl<GS> TreeNode<GS> where GS: GameState {
pub fn expand(&mut self, game: &GS::G){
if !self.expanded{
let child_states = self.data.generate_children(game);
for state in child_states{
self.add_child_with_value(state);
}
}
}
}

The concrete type of G cannot be detemined based on the type of TreeNode<GS>; it is only known when expand is called. Note that expand could be called twice with different types for G.
You can express this by constraining the type parameters for the method instead of the entire implementation block:
impl<GS> TreeNode<GS> {
pub fn expand<G>(&mut self, game: &G)
where
G: Game,
GS: GameState<G>,
{
if !self.expanded {
let child_states = self.data.generate_children(game);
for state in child_states {
self.add_child_with_value(state);
}
}
}
}
If it should not be possible for expand to be called with different Gs then this is a problem of your modeling. Another way to fix this is to ensure that the type of G is known for all TreeNodes. e.g.:
struct TreeNode<G, S>
where
S: Sized,
{
parent: *mut TreeNode<G, S>,
expanded: bool,
pub children: Vec<TreeNode<G, S>>,
pub data: S,
pub n: u32,
}
And then your original implementation block should work as written, once you account for the extra type parameter.

Related

Implementing a dynamic-typed LinkedList in Rust

This is a follow-up on the question asked here: Possible to implement dynamically-typed linked list in safe Rust?
I successfully implemented a dynamic type LinkedList using the std::any::Any trait.
However, I want to challenge myself by trying to implement it in another way, e.g. using generic type - Node where T can be any type, u32, u64, String, ...
Example
Node<String> -> Node<u32> -> Node<u64> -> Node<String> -> ...
My approach is to use a trait called Next to give Node<T> the ability to "go next".
Node<T> looks like this.
struct Node<T> {
data: T,
next: Option<Rc<RefCell<dyn Next>>>,
}
The trait Next looks like this.
pub trait Next {
fn borrow_next(&self) -> Option<Ref<dyn Next>>;
fn set_next(&mut self, next: Rc<RefCell<dyn Next>>);
}
These are the implementation of Next for any Node.
impl<T> Next for Node<T> {
fn set_next(&mut self, next: Rc<RefCell<dyn Next>>) {
self.next = Some(next);
}
fn borrow_next(&self) -> Option<Ref<dyn Next>> {
match &self.next {
None => None,
Some(stmt) => Some(stmt.borrow()),
}
}
}
Here are the implementations for the actual struct Node<T>.
impl<T> Node<T> {
pub fn new<P>(data: P) -> Node<P> {
Node::<P> { data, next: None }
}
pub fn new_wrapped<P>(data: P) -> Rc<RefCell<Node<P>>> {
Rc::new(RefCell::new(Node::<P>::new(data)))
}
pub fn into_wrapped(self) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(self))
}
pub fn borrow_data(&self) -> &T {
&self.data
}
pub fn set_data(&mut self, data: T) {
self.data = data;
}
}
Lastly, the declaration and its implementations of methods of struct DynLinkedList, holding two fields, head and tail, look like this.
struct DynLinkedList {
head: Option<Rc<RefCell<dyn Next>>>,
tail: Option<Rc<RefCell<dyn Next>>>,
}
impl DynLinkedList {
pub fn new_empty() -> Self {
Self {
head: None,
tail: None,
}
}
pub fn new_with_node(node: Rc<RefCell<dyn Next>>) -> Self {
Self {
head: Some(node.clone()),
tail: Some(node),
}
}
pub fn append(&mut self, node: Rc<RefCell<dyn Next>>) {
self.tail.take().map_or_else(
|| self.head = Some(node.clone()),
|old_tail| old_tail.borrow_mut().set_next(node.clone()),
);
self.tail = Some(node);
}
}
Here comes the problem:
I am unable to access the data field of Node<T> as it is being treated as a trait object dyn Next by the compiler.
For example, this test would not work:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dynll_new_with_node() {
let node = Node::<u32>::new(77_u32);
let dynll = DynLinkedList::new_with_node(node.into_wrapped());
assert_eq!(&dynll.head.unwrap().borrow().borrow_data(), &77);
assert_eq!(&dynll.tail.unwrap().borrow().borrow_data(), &77)
}
}
The compiler error is:
error[E0599]: no method named `borrow_data` found for struct `Ref<'_, (dyn Next + 'static)>` in the current scope
--> src/dyn_ll_idea_five.rs:125:47
|
125 | assert_eq!(&*dynll.head.unwrap().borrow().borrow_data(), &77);
| ^^^^^^^^^^^ method not found in `Ref<'_, (dyn Next + 'static)>`
But, when the .borrow() after .unwrap() returns, it should return an object of type Node which would have the method .borrow_data(), how can I let Rust know that this is the case? Thank you.
I would effectively want to be able to do this:
let mut list = DynLinkedList::new();
list.push_front("hello".to_string());
list.push_back("world".to_string());
list.push_front(123);
list.push_back(456);
assert_eq!(list.pop_front(), Some("hello".to_string()));
assert_eq!(list.pop_back(), Some("world".to_string()));
assert_eq!(list.pop_front(), Some(123));
assert_eq!(list.pop_back(), Some(456));
Well, nowhere in the definition of trait Next does it talk about objects of type Node. Thus, how would the compiler ever know that you can call borrow_data on it? That's where you'd do the downcast via the Any trait.
What's more, the compiler would also want to know which sort of Node we're talking about. Node<i32> or Node<String> or what? And that's downright impossible because your list is dynamic and hence whatever type is contained within a node is also dynamic.
Let's take your example:
Node<String> -> Node<u32> -> Node<u64> -> Node<String> -> ...
So if that's your list, then, using very rough ugly pseudocode, what about this:
let x: String = my_list.head.borrow_data();
let y: u32 = my_list.head.next.borrow_data();
let z: u64 = my_list.head.next.next.borrow_data();
You see the problem here? How is the compiler to know, at compile time, that the third item in the list has type u64? This just isn't a case where generics work in the way you want it.

How to solve "expected struct, found type parameter" on assignment?

Just started working with Rust a couple of days ago. I'm porting some C++ code right now, and this question seems to be the reverse of the common "expected struct, got type" sort. This code involves two classes, a container class A and a client class B.
use std::vec::Vec;
struct A<T:FooTrait> {
children: Vec<*mut T>
}
impl <T:FooTrait> A<T> {
fn insert(&mut self, val: &mut T) -> Handle<T> {
self.children.push(val);
return Handle{owner: self};
}
}
struct B {
handle: Handle<B>
}
trait FooTrait {
fn set_handle<T:FooTrait>(&mut self, h: Handle<T>);
}
impl FooTrait for B {
fn set_handle<B:FooTrait>(&mut self, h: Handle<B>) {
self.handle = h; // <-- Here is the problem
}
}
struct Handle<T:FooTrait> {
owner: *mut A<T>
}
impl <T:FooTrait> Default for Handle<T> {
fn default()->Handle<T> {
Handle {
owner: std::ptr::null_mut()
}
}
}
fn main() {
let mut foo = A::<B> { children: Default::default() };
let mut b = B{handle: Default::default()};
b.handle = foo.insert(&mut b);
}
Getting the error:
error[E0308]: mismatched types
--> src/main.rs:23:23
|
22 | fn set_handle<B:FooTrait>(&mut self, h: Handle<B>) {
| - this type parameter
23 | self.handle = h;
| ^ expected struct `B`, found type parameter `B`
|
= note: expected struct `Handle<B>` (struct `B`)
found struct `Handle<B>` (type parameter `B`)
Simplified version (playground):
use std::marker::PhantomData;
struct B {
handle: PhantomData<B>,
}
trait FooTrait {
fn set_handle<T: FooTrait>(&mut self, h: PhantomData<T>);
}
impl FooTrait for B {
fn set_handle<BType: FooTrait>(&mut self, h: PhantomData<BType>) {
self.handle = h;
}
}
Note that I've changed the name of the type parameter in set_handle. Now the error is more clear:
error[E0308]: mismatched types
--> src/lib.rs:13:23
|
12 | fn set_handle<BType: FooTrait>(&mut self, h: PhantomData<BType>) {
| ----- this type parameter
13 | self.handle = h;
| ^ expected struct `B`, found type parameter `BType`
|
= note: expected struct `std::marker::PhantomData<B>`
found struct `std::marker::PhantomData<BType>`
In your case, the error is essentially the same, since the generic parameter is a new type, which shadowed the global struct B.
Now, what to do? It depends on what do you want to get.
If the struct B definition is correct and set_handle need to handle only Bs, just remove the generic parameter from set_handle (playground):
trait FooTrait {
fn set_handle(&mut self, h: PhantomData<B>);
}
impl FooTrait for B {
fn set_handle(&mut self, h: PhantomData<B>) {
self.handle = h;
}
}
If the struct B definition is correct, but set_handle must be able to use different handler types depending on Self, use an associated type (playground):
trait FooTrait {
type T: FooTrait;
fn set_handle(&mut self, h: PhantomData<Self::T>);
}
impl FooTrait for B {
type T = B;
fn set_handle(&mut self, h: PhantomData<B>) {
self.handle = h;
}
}
Now the implementation block will choose what kind of argument (handler, in your case) it will get.
If the set_handle definition is correct, i.e. the caller can choose the type of handler, then struct B must be generic, too. However, in this case you essentially can't use the trait-based approach, since the trait has to be generic too, and you will not be able to simply use it in any generic bound without providing parameters (which has to be bound too, ad infinitum).
There are two different B here. The original B you defined in struct B, and the second <B:FooTrait> type parameter. The second is shadowing the first. Here is how the compiler sees it:
impl FooTrait for B {
fn set_handle<AnyFoo:FooTrait>(&mut self, h: Handle<AnyFoo>) {
self.handle = h; // <-- Here is the problem
}
}
The way you've defined things, FooTrait::set_handle is supposed to work with any type that implements FooTrait, but the handle field of B can only store a Handle<B>. You need to rethink what you want FooTrait to do and how B satisfies it.

How to use dynamic dispatch with a method which takes an iterator as a parameter?

I am writing a command line application in rust for processing audio from a sensor. I would like the user to be able to choose an algorithm or filter to apply from several options. I was hoping to use dynamic dispatch to switch out a struct which implements my filter trait at runtime. However, this is not allowed by the compiler, because one of the trait methods takes a generic parameter.
How could I implement this same functionality, without causing any compiler troubles? I know that an easy solution is to change the parameter of the process method to an array or a vector, but this is my last resort, as I would much prefer to take an iterator or an IntoIterator, as it is more general, and suits my specific needs.
Here is some code which demonstrates the problem.
trait SensorFilter {
fn process(&self, sig: &mut impl Iterator<Item = f32>) -> Vec<f32>;
}
struct Alg1 {
mul: f32,
}
struct Alg2 {
add: f32,
}
impl SensorFilter for Alg1 {
fn process(&self, sig: &mut impl Iterator<Item = f32>) -> Vec<f32> {
sig.map(|x| x * self.mul).collect()
}
}
impl SensorFilter for Alg2 {
fn process(&self, sig: &mut impl Iterator<Item = f32>) -> Vec<f32> {
sig.map(|x| x * self.add).collect()
}
}
enum AlgChoice {
Alg1,
Alg2
}
fn main() {
let choice = AlgChoice::Alg1; // user chooses via command-line.
let mut sig = vec![0.,1.,2.,3.,4.,5.,6.].into_iter(); // iterator gets data from sensor.
// This doesn't work, because my trait cannot be made into an object.
let alg: &dyn SensorFilter = match choice {
AlgChoice::Alg1 => Alg1{mul:0.3},
_ => Alg2{add:1.2},
};
let result = alg.process(&mut sig);
println!("{:?}",result);
}
Thanks :)
The trick here is to change your generic function parameter to a generic trait parameter:
// Make the generic param into a type argument w/ constraints
trait SensorFilter<I> where I: Iterator<Item = f32> {
fn process(&self, sig: &mut I) -> Vec<f32>;
}
struct Alg1 {
mul: f32,
}
struct Alg2 {
add: f32,
}
// Implement trait for all I that match the iterator constraint
impl<I: Iterator<Item = f32>> SensorFilter<I> for Alg1 {
fn process(&self, sig: &mut I) -> Vec<f32> {
sig.map(|x| x * self.mul).collect()
}
}
impl<I: Iterator<Item = f32>> SensorFilter<I> for Alg2 {
fn process(&self, sig: &mut I) -> Vec<f32> {
sig.map(|x| x * self.add).collect()
}
}
enum AlgChoice {
Alg1,
Alg2
}
fn main() {
let choice = AlgChoice::Alg1; // user chooses via command-line.
let mut sig = vec![0.,1.,2.,3.,4.,5.,6.].into_iter(); // iterator gets data from sensor.
// Specify the type argument of your trait.
let alg: &dyn SensorFilter<std::vec::IntoIter<f32>> = match choice {
AlgChoice::Alg1 => &Alg1{mul:0.3},
_ => &Alg2{add:1.2},
};
let result = alg.process(&mut sig);
println!("{:?}",result);
}
The simplest way to make SensorFilter object safe is to simply change process to accept dyn Iterator instead of impl Iterator:
trait SensorFilter {
fn process(&self, sig: &mut dyn Iterator<Item = f32>) -> Vec<f32>;
}
If you couldn't do this, for example because Iterator were actually non-object-safe, you could instead extract the common, non-object-safe part into a second trait, and implement it automatically for everything that is SensorFilter:
// This trait is object-safe.
trait SensorFilter {
fn filter(&self, x: f32) -> f32;
}
// This trait will not be object-safe because it uses generics.
trait Process {
fn process<I: IntoIterator<Item = f32>>(&self, sig: I) -> Vec<f32>;
}
// The `?Sized` bound allows you to call `.process()` on `dyn SensorFilter`.
impl<T: ?Sized + SensorFilter> Process for T {
fn process<I: IntoIterator<Item = f32>>(&self, sig: I) -> Vec<f32> {
sig.into_iter().map(|x| self.filter(x)).collect()
}
}
// ...
impl SensorFilter for Alg1 {
fn filter(&self, x: f32) -> f32 {
x * self.mul
}
}
impl SensorFilter for Alg2 {
fn filter(&self, x: f32) -> f32 {
x * self.add
}
}
Playground
Note that instead of Iterator I used IntoIterator, which is strictly more general.
A variation on this idea, when you can't easily remove the genericity from SensorFilter, is to use double dispatch: write SensorFilter to use dyn Iterator instead of impl Iterator, and then write a convenience trait that just wraps it with the specific type:
trait SensorFilter {
fn process_dyn(&self, sig: &mut dyn Iterator<Item = f32>) -> Vec<f32>;
}
trait Process {
fn process<I: IntoIterator<Item = f32>>(&self, sig: I) -> Vec<f32>;
}
impl<T: ?Sized + SensorFilter> Process for T {
fn process<I: IntoIterator<Item = f32>>(&self, sig: I) -> Vec<f32> {
self.process_dyn(&mut sig.into_iter())
}
}

Is it possible to use `impl Trait` as a function's return type in a trait definition?

Is it at all possible to define functions inside of traits as having impl Trait return types? I want to create a trait that can be implemented by multiple structs so that the new() functions of all of them returns an object that they can all be used in the same way without having to write code specific to each one.
trait A {
fn new() -> impl A;
}
However, I get the following error:
error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
--> src/lib.rs:2:17
|
2 | fn new() -> impl A;
| ^^^^^^
Is this a limitation of the current implementation of impl Trait or am I using it wrong?
As trentcl mentions, you cannot currently place impl Trait in the return position of a trait method.
From RFC 1522:
impl Trait may only be written within the return type of a freestanding or inherent-impl function, not in trait definitions or any non-return type position. They may also not appear in the return type of closure traits or function pointers, unless these are themselves part of a legal return type.
Eventually, we will want to allow the feature to be used within traits [...]
For now, you must use a boxed trait object:
trait A {
fn new() -> Box<dyn A>;
}
See also:
Is it possible to have a constructor function in a trait?
Why can a trait not construct itself?
How do I return an instance of a trait from a method?
Nightly only
If you wish to use unstable nightly features, you can use existential types (RFC 2071):
// 1.67.0-nightly (2022-11-13 e631891f7ad40eac3ef5)
#![feature(type_alias_impl_trait)]
#![feature(return_position_impl_trait_in_trait)]
trait FromTheFuture {
type Iter: Iterator<Item = u8>;
fn returns_associated_type(&self) -> Self::Iter;
// Needs `return_position_impl_trait_in_trait`
fn returns_impl_trait(&self) -> impl Iterator<Item = u16>;
}
impl FromTheFuture for u8 {
// Needs `type_alias_impl_trait`
type Iter = impl Iterator<Item = u8>;
fn returns_associated_type(&self) -> Self::Iter {
std::iter::repeat(*self).take(*self as usize)
}
fn returns_impl_trait(&self) -> impl Iterator<Item = u16> {
Some((*self).into()).into_iter()
}
}
fn main() {
for v in 7.returns_associated_type() {
println!("type_alias_impl_trait: {v}");
}
for v in 7.returns_impl_trait() {
println!("return_position_impl_trait_in_trait: {v}");
}
}
If you only need to return the specific type for which the trait is currently being implemented, you may be looking for Self.
trait A {
fn new() -> Self;
}
For example, this will compile:
trait A {
fn new() -> Self;
}
struct Person;
impl A for Person {
fn new() -> Person {
Person
}
}
Or, a fuller example, demonstrating using the trait:
trait A {
fn new<S: Into<String>>(name: S) -> Self;
fn get_name(&self) -> String;
}
struct Person {
name: String
}
impl A for Person {
fn new<S: Into<String>>(name: S) -> Person {
Person { name: name.into() }
}
fn get_name(&self) -> String {
self.name.clone()
}
}
struct Pet {
name: String
}
impl A for Pet {
fn new<S: Into<String>>(name: S) -> Pet {
Pet { name: name.into() }
}
fn get_name(&self) -> String {
self.name.clone()
}
}
fn main() {
let person = Person::new("Simon");
let pet = Pet::new("Buddy");
println!("{}'s pets name is {}", get_name(&person), get_name(&pet));
}
fn get_name<T: A>(a: &T) -> String {
a.get_name()
}
Playground
As a side note.. I have used String here in favor of &str references.. to reduce the need for explicit lifetimes and potentially a loss of focus on the question at hand. I believe it's generally the convention to return a &str reference when borrowing the content and that seems appropriate here.. however I didn't want to distract from the actual example too much.
You can get something similar even in the case where it's not returning Self by using an associated type and explicitly naming the return type:
trait B {}
struct C;
impl B for C {}
trait A {
type FReturn: B;
fn f() -> Self::FReturn;
}
struct Person;
impl A for Person {
type FReturn = C;
fn f() -> C {
C
}
}
Fairly new to Rust, so may need checking.
You could parametrise over the return type. This has limits, but they're less restrictive than simply returning Self.
trait A<T> where T: A<T> {
fn new() -> T;
}
// return a Self type
struct St1;
impl A<St1> for St1 {
fn new() -> St1 { St1 }
}
// return a different type
struct St2;
impl A<St1> for St2 {
fn new() -> St1 { St1 }
}
// won't compile as u32 doesn't implement A<u32>
struct St3;
impl A<u32> for St3 {
fn new() -> u32 { 0 }
}
The limit in this case is that you can only return a type T that implements A<T>. Here, St1 implements A<St1>, so it's OK for St2 to impl A<St2>. However, it wouldn't work with, for example,
impl A<St1> for St2 ...
impl A<St2> for St1 ...
For that you'd need to restrict the types further, with e.g.
trait A<T, U> where U: A<T, U>, T: A<U, T> {
fn new() -> T;
}
but I'm struggling to get my head round this last one.

How can I explicitly specify a lifetime when implementing a trait?

Given the implementation below, where essentially I have some collection of items that can be looked up via either a i32 id field or a string field. To be able to use either interchangeably, a trait "IntoKey" is used, and a match dispatches to the appropriate lookup map; this all works fine for my definition of get within the MapCollection impl:
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");
}
}
However, if I then want to implement std::ops::Index for this struct, if I attempt to do the below:
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")
}
}
I hit a compiler error:
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 }
I can find no way to specify a distinct lifetime here; following the compiler's recommendation is not permitted as it changes the function signature and no longer matches the trait, and anything else I try fails to satisfy the lifetime specification.
I understand that I can implement the trait for each case (i32, String) separately instead of trying to implement it once for IntoKey, but I am more generally trying to understand lifetimes and appropriate usage. Essentially:
Is there actually an issue the compiler is preventing? Is there something unsound about this approach?
Am I specifying my lifetimes incorrectly? To me, the lifetime 'a in Key/IntoKey is dictating that the reference need only live long enough to do the lookup; the lifetime 'b associated with the index fn is stating that the reference resulting from the lookup will live as long as the containing MapCollection.
Or am I simply not utilizing the correct syntax to specify the needed information?
(using rustc 1.0.0-nightly (b63cee4a1 2015-02-14 17:01:11 +0000))
Do you intend on implementing IntoKey on struct's that are going to store references of lifetime 'a? If not, you can change your trait and its implementations to:
trait IntoKey {
fn into_key<'a>(&'a self) -> Key<'a>;
}
This is the generally recommended definition style, if you can use it. If you can't...
Let's look at this smaller reproduction:
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() {
}
The problem lies in get:
fn get<'a, K>(&self, key: &'a K) -> &u8
where K: IntoKey<'a>
Here, we are taking a reference to K that must live as long as the Key we get out of it. However, the Index trait doesn't guarantee that:
fn index<'b>(&'b self, k: &K) -> &'b u8
You can fix this by simply giving a fresh lifetime to key:
fn get<'a, 'b, K>(&self, key: &'b K) -> &u8
where K: IntoKey<'a>
Or more succinctly:
fn get<'a, K>(&self, key: &K) -> &u8
where K: IntoKey<'a>

Resources