Synesso
Synesso

Reputation: 38978

Type hinting on Rust function calls

I’m trying to extract a function from a struct. The original (working) is:

pub fn f(&self, id: Id) -> &[Foo] {
    self.foos.get(&id).map_or(&[], |foos| &**foos)
}

Where where self.foos is a HashMap<Id, Vec<Foo>, BuildHasherDefault<FnvHasher>>

After extraction I have

fn f<'a>(foos: HashMap<Id, Vec<Foo>, BuildHasherDefault<FnvHasher>>, id: Id) -> &'a [Foo] {
    &foos.get(&id).map_or(&[], |xs| &**xs);
}

The resulting error is

error: mismatched types:
 expected `&[_; 0]`,
  found `&[foo::Foo]`
 (expected array of 0 elements,
   found slice) [E0308]

I think I need to provide a type hint. Is this right? If so, how do I explicitly declare the type of U when calling Option.map_or<U, F>(self, default: U, f: F) -> U

Upvotes: 1

Views: 1125

Answers (1)

Francis Gagn&#233;
Francis Gagn&#233;

Reputation: 65782

The error you're getting hides the real issues with the code. In situations like this, by being more explicit, the compiler can get further and give other errors that are more relevant to solving the problem.

First, let's get rid of the type mismatch by explicitly converting the array to a slice, by changing &[] to &[][..].

fn f<'a>(foos: HashMap<Id, Vec<Foo>, BuildHasherDefault<FnvHasher>>, id: Id) -> &'a [Foo] {
    &foos.get(&id).map_or(&[][..], |xs| &**xs);
}

We're now getting this error:

error: not all control paths return a value [--explain E0269]
  --> <anon>:16:1
   |>
16 |> fn f<'a>(foos: HashMap<Id, Vec<Foo>, BuildHasherDefault<FnvHasher>>, id: Id) -> &'a [Foo] {
   |> ^

Ah ha! There's a semicolon at the end of the function that shouldn't be there. Let's remove it.

fn f<'a>(foos: HashMap<Id, Vec<Foo>, BuildHasherDefault<FnvHasher>>, id: Id) -> &'a [Foo] {
    &foos.get(&id).map_or(&[][..], |xs| &**xs)
}

Mmm, still not compiling:

error: `foos` does not live long enough
  --> <anon>:17:6
   |>
17 |>     &foos.get(&id).map_or(&[][..], |xs| &**xs)
   |>      ^^^^
note: reference must be valid for the lifetime 'a as defined on the block at 16:90...
  --> <anon>:16:91
   |>
16 |> fn f<'a>(foos: HashMap<Id, Vec<Foo>, BuildHasherDefault<FnvHasher>>, id: Id) -> &'a [Foo] {
   |>                                                                                           ^
note: ...but borrowed value is only valid for the scope of function body at 16:90
  --> <anon>:16:91
   |>
16 |> fn f<'a>(foos: HashMap<Id, Vec<Foo>, BuildHasherDefault<FnvHasher>>, id: Id) -> &'a [Foo] {
   |>                                                                                           ^

Basically, the error is that you're trying to return a borrowed pointer into a HashMap that will be destroyed at the end of the call to f (because the HashMap is passed by value). You need to pass the HashMap by reference. We can get rid of the unnecessary [..] now, because it compiles!

fn f(foos: &HashMap<Id, Vec<Foo>, BuildHasherDefault<FnvHasher>>, id: Id) -> &[Foo] {
    foos.get(&id).map_or(&[], |xs| &**xs)
}

Upvotes: 8

Related Questions