0

I am trying to write the function set which calls the Rust LMDB library (docs), and an example I'm working off of.

I can't for the life of me get this to work. Here is my current attempt:

fn main() {
    let env = getenv("duperdb");
    let dbhandle = get_dbhandle("", &env);
    let txn = new_transaction(&env);
    let vec = vec![("foo", "another text"), ("bar", "and another")];
    set(&dbhandle, &env, &vec);

    let reader = env.get_reader().unwrap();
    let db = reader.bind(&dbhandle);
    let note = db.get::<&str>("foo").unwrap();

    println!("NOTE: {}", note);
}

Where set is defined as:

pub fn set<A: ToMdbValue, B: ToMdbValue>(
    handle: &DbHandle,
    env: &Environment,
    pairs: &Vec<(&A, &B)>) -> () {

    let txn = new_transaction(&env);

    {
        let db = txn.bind(&handle);

        for &(id, note) in pairs.iter() {
            db.set(&id, &note).unwrap();
        }
    }

    match txn.commit() {
            Err(_) => panic!("Failed to commit!"),
            Ok(_) => (),
    }
}

This spits out the following error:

src/db/wrapper.rs:28:20: 28:23 error: the trait `lmdb::traits::ToMdbValue` is not implemented for the type `&A` [E0277]
src/db/wrapper.rs:28             db.set(&id, &note).unwrap();
                                        ^~~

I also tried db.set(id, note).unwrap();, but this time I get:

src/main.rs:13:5: 13:8 error: the trait `core::marker::Sized` is not implemented for the type `str` [E0277]
src/main.rs:13     set(&dbhandle, &env, &vec);
                   ^~~
src/main.rs:13:5: 13:8 help: run `rustc --explain E0277` to see a detailed explanation
src/main.rs:13:5: 13:8 note: `str` does not have a constant size known at compile-time
src/main.rs:13:5: 13:8 note: required by `dupernote::db::wrapper::set`
src/main.rs:13:5: 13:8 error: the trait `lmdb_rs::traits::ToMdbValue` is not implemented for the type `str` [E0277]
src/main.rs:13     set(&dbhandle, &env, &vec);
                   ^~~

I also tried stuff like:

    for (id, note) in pairs.iter() {
        db.set(id, note).unwrap();
    }

But that doesn't work either... I don't fully understand why. Doesn't id and note have type &str, not str?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
o_o_o--
  • 975
  • 9
  • 20

3 Answers3

1

Here's an MCVE of your problem:

trait Example {}

impl Example for i32 {}

fn library_call<T>(value: T)
    where T: Example,
{}

fn user_call<T>(values: &[T])
    where T: Example,
{
    for i in values {
        library_call(i);
    }
}

fn main() {
    let values = vec![1, 2, 3];
    user_call(&values);
}

With the error:

error: the trait `Example` is not implemented for the type `&T` [E0277]

library_call(i);
^~~~~~~~~~~~

The error message is exactly correct - Example is not implemented for &T, it's only guaranteed to be implemented for T. &T and T are different types.

Instead, you need to indicate that a reference to the generic type implements the trait you need:

fn user_call<T>(values: &[T])
    where for <'a> &'a T: Example,

And then you need to make sure that a reference to the concrete type actually implements the trait:

impl<'a> Example for &'a i32 {}

Or a broader version:

impl<'a, T> Example for &'a T
    where T: Example
{}

See also When should I not implement a trait for references to implementors of that trait?

Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • :( I'm getting more confused. So I added `impl<'a, T> ToMdbValue for &'a T where T: ToMdbValue {}`, which throws error `E0210`. Reading the rustc explanation, I add `struct MyType(T);` and adjust like in the example. Now I get "not all trait items implemented, missing: `to_mdb_value`" ... this is going down a rabbit hole at this point. I didn't simplify the example because I don't fully understand the problem. – o_o_o-- Mar 15 '16 at 17:48
  • Also: `ToMdbValue` is implemented for `&'a str` according to the docs. – o_o_o-- Mar 15 '16 at 17:56
  • @o_o_o-- try changing your `set` to have generic constraints like my `user_call`: `where for <'a> &'a A: ToMdbValue, for <'a> &'a B: ToMdbValue,`. – Shepmaster Mar 15 '16 at 18:01
  • @o_o_o-- also try `pairs: &[(A, B)]` (you do want two different types, right?). There's never a real need for `&Vec`. – Shepmaster Mar 15 '16 at 18:05
  • I'm passing it two `&str`, I originaly had 2 types because I wanted it to also allow for ints, but for now I just want to get it to work as is. -- `E0210` yells at me because `ToMdbValue` is defined in an external crate. Hence the added `MyType` stuff, but it just keeps complicating further... – o_o_o-- Mar 15 '16 at 18:07
  • I got it working (see my answer bellow) by adding one more level of indirection to the variables being passed. Thanks for the help! – o_o_o-- Mar 15 '16 at 20:06
1

The definition of the function that gives you an error (if I'm reading the docs right):

fn set(&self, key: &ToMdbValue, value: &ToMdbValue) -> MdbResult<()>

key must be a reference to a trait object. You are trying to pass a reference to a generic type implmementing ToMdbValue.

https://doc.rust-lang.org/book/trait-objects.html

I can't verify but this should work:

pub fn set(handle: &DbHandle, env: &Environment, pairs: &Vec<(&ToMdbValue, &ToMdbValue)>) -> () {

    let txn = new_transaction(&env);

    {
        let db = txn.bind(&handle);

        for &(id, note) in pairs.iter() {
            db.set(id, note).unwrap();
        }
    }

    match txn.commit() {
            Err(_) => panic!("Failed to commit!"),
            Ok(_) => (),
    }
}

Other things: you may want to work with boxed trait objects Box<ToMdbValue>. The link above explains it. You should pass a &[YourType] rather than &Vec<[YourType]>.

A.B.
  • 15,364
  • 3
  • 61
  • 64
  • I get mismatched type: "expected `&collections::vec::Vec<(&lmdb_rs::traits::ToMdbValue, &lmdb_rs::traits::ToMdbValue)>`, found `&collections::vec::Vec<(&str, &str)>`" -- I was getting this before hence I assumed I had to pass a generic that implements `ToMdbValue` :( – o_o_o-- Mar 15 '16 at 17:28
  • @o_o_o--: this means that you have to change the call site of `set` too, to now pass a `Vec<(&ToMdbValue, &ToMdbValue)>` instead of a `Vec<(&str, &str)>)` => the changes are bubbling up. – Matthieu M. Mar 16 '16 at 07:21
1

I managed to get it working. I'm not sure how kosher this solution is, but I'll post it.

So now, in main(), I do the following (example with an (int, string) kv pair):

let k = 1;
let val = "hello there";
let vec = vec![(&k, &val)];
set(&dbhandle, &env, &vec);

I had to declare them separately since vec![(&1, &"hello there")] threw an error of the form borrowed value does not live long enough.

set now looks like this:

pub fn set<A, B>(handle: &DbHandle, env: &Environment, pairs: &Vec<(&A, &B)>)
    -> ()
    where A: ToMdbValue,
          B: ToMdbValue {

    let txn = new_transaction(&env);

    {
        let db = txn.bind(&handle);

        for &(id, note) in pairs.iter() {
            db.set(id, note).unwrap();
        }
    }

    match txn.commit() {
            Err(_) => panic!("Failed to commit!"),
            Ok(_) => (),
    }
}
o_o_o--
  • 975
  • 9
  • 20