0
votes

This works:

fn user_add<'x>(data: &'x Input, db: &'x mut Database<'x>) -> HandlerOutput {
    //let input: UserAddIn = json::decode(&data.post).unwrap();
    //let username = input.username.as_bytes();
    //let password = input.password.as_bytes();
    db.put(b"Hi", b"hello");
    //db.delete(username);
    Ok("Hi".to_string())
}

This does not work:

fn user_add<'x>(data: &'x Input, db: &'x mut Database<'x>) -> HandlerOutput {
    //let input: UserAddIn = json::decode(&data.post).unwrap();
    //let username = input.username.as_bytes();
    //let password = input.password.as_bytes();
    let my_str = "hi".to_string();
    let username = my_str.as_bytes();
    db.put(username, b"hello");
    //db.delete(username);
    Ok("Hi".to_string())
}

Compiler output:

src/handlers.rs:85:17: 85:23 error: `my_str` does not live long enough
src/handlers.rs:85      let username = my_str.as_bytes();
                                       ^~~~~~
src/handlers.rs:80:77: 89:2 note: reference must be valid for the lifetime 'x as defined on the block at 80:76...
src/handlers.rs:80 fn user_add<'x>(data: &'x Input, db: &'x mut Database<'x>) -> HandlerOutput {
src/handlers.rs:81      //let input: UserAddIn = json::decode(&data.post).unwrap();
src/handlers.rs:82      //let username = input.username.as_bytes();
src/handlers.rs:83      //let password = input.password.as_bytes();
src/handlers.rs:84      let my_str = "hi".to_string();
src/handlers.rs:85      let username = my_str.as_bytes();
                   ...
src/handlers.rs:84:32: 89:2 note: ...but borrowed value is only valid for the block suffix following statement 0 at 84:31
src/handlers.rs:84      let my_str = "hi".to_string();
src/handlers.rs:85      let username = my_str.as_bytes();
src/handlers.rs:86      db.put(username, b"hello");
src/handlers.rs:87      //db.delete(username);
src/handlers.rs:88      Ok("Hi".to_string())
src/handlers.rs:89 }

I've seen several questions about lifetime in Rust and I think the book is not that clear about it. I still use lifetimes as trial and error. This specific case has confused me because I've made several attempts fighting against the compiler and this is just the last error I got. If you have some Rust skills please consider editing the part about lifetimes in the book.

2

2 Answers

3
votes

In the first case b"Hi" is a byte literal, and has type &'static [u8] which means “slice of u8 with infinite lifetime”. The function put needs some lifetime 'x, since 'static live is bigger than any lifetime, Rust is happy to use it.

In the second case

let my_str = "hi".to_string();
let username = my_str.as_bytes();

username is a reference to the inner buffer of my_str and cannot outlive it. The compiler complains because the first argument of put should have a lifetime 'x which is broader than that of my_str (local to user_add). Rust won't allow you to do that because db would point to dangling data at the end of the function call:

user_add(input, &mut db);
// `my_str` was local to `user_add` and doesn't exist anymore
// if Rust had allowed you to put it in `db`, `db` would now contain some invalid data here
1
votes

Thanks to @mcarton for answering why the error happens. In this answer I hope it becames clear how to solve it too.


The compiler's code generation is perfect but the error message is just terribly confusing to me.

The problem was in another library that I made, that happens to be a database. The database struct contains an entry that holds slices. The lifetime of the slices was set as:

struct Entry<'a> {
    key: &'a [u8],
    value: &'a [u8],
}

pub struct Database<'a> {
    file: File,
    entries: Vec<Entry<'a>>,
}

It means that the data that the slice holds need to live longer than the database struct. The username variable goes out of scope but the database holding a reference to it still lives. So it means that the database would have to hold data that lives longer than it, like static variables, which makes the database useless.

The library compiled okay. But the error showed elsewhere.

The solution for that was to exchange the slices for vectors because vectors are not pointers. The vectors can live less than the database.

struct Entry {
    key: Vec<u8>,
    value: Vec<u8>,
}

pub struct Database {
    file: File,
    entries: Vec<Entry>,
}