user
user

Reputation: 5390

Bounds on associated types but 'error: the type of this value must be known in this context'

The following

use std::borrow::Borrow;

trait DictLike<'a> {
    type Label: Eq + 'a;
    type ItemsIterator: Iterator<Item=(&'a Self::Label, &'a DictLike<'a>)> + 'a;

    fn items(&self) -> Self::ItemsIterator;

    fn get<Q: ?Sized + Eq>(&self, key: &Q) -> Option<&'a DictLike<'a>>
        where Self::Label: Borrow<Q> {
        for (k,v) in self.items() {
            if k.borrow().eq(key) {
                return Some(v);
            }
        }
        None
    }
}

errors out as

lib.rs:12:16: 12:26 error: the type of this value must be known in this context
lib.rs:12             if k.borrow().eq(key) {
                         ^~~~~~~~~~

Why? Doesn't ItemsIterator's type bound and the bound Self::Label: Borrow<Q> provide the necessary type information?

Upvotes: 1

Views: 160

Answers (1)

huon
huon

Reputation: 102306

This seems like a bug: the <'a> in the trait declaration is seemingly causing the compiler to get confused, e.g.:

trait DictLike<'a> {
    type ItemsIterator: Iterator<Item=u8>;

    fn items(&self) -> Self::ItemsIterator;

    fn get(&self)  {
        for k in self.items() {}
    }
}
<anon>:7:13: 7:14 error: unable to infer enough type information about `_`; type annotations required [E0282]
<anon>:7         for k in self.items() {}
                     ^

Removing the 'a allows that code to compile. I've filed #24338 about this.

Fortunately, there is a work around: this seems to only happen when the code is inside the default method, and moving the actual implementation to an external function works fine:

use std::borrow::Borrow;

trait DictLike<'a> {
    type Label: Eq + 'a;
    type ItemsIterator: Iterator<Item=(&'a Self::Label, &'a DictLike<'a>)> + 'a;

    fn items(&self) -> Self::ItemsIterator;

    fn get<Q: ?Sized + Eq>(&self, key: &Q) -> Option<&'a DictLike<'a>>
            where Self::Label: Borrow<Q> {
        get(self, key)
    }
}

fn get<'a, X: ?Sized + DictLike<'a>, Q: ?Sized + Eq>(x: &X, key: &Q) -> Option<&'a DictLike<'a>>
        where X::Label: Borrow<Q> {
    for (k,v) in x.items() {
        if k.borrow() == key {
            return Some(v);
        }
    }
    None
}

This is functionally no different to the original code, except for the fact that it compiles. (I took the liberty of switching the .eq call to the == operator, which is nicer sugar for the method call.)

Also, if you're feeling like experimenting with iterators a little more, one could write get as

x.items()
 .find(|&(k,_)| k.borrow() == key)
 .map(|(_, v)| v)

Using Iterator::find and Option::map.

Upvotes: 3

Related Questions