Help understand borrowing scenario

Hi,

I am new to Rust and implementing some exercises to understand borrowing and there is a situation that I don't get why happen. Bellow the code:

struct User {
    name:String,
    age: i8,
    gender: char,
    email:String
}


impl User {
    fn birthday(&mut self) {
        self.age += 1;
    }

    fn create(name:String, age:i8, gender:char, email:String) -> User {
        User { name, age, gender, email }
    }

    fn name_abv(&self) -> String {
        let chars = self.name.as_bytes();
        let mut abv = String::new();
        for (i, &c) in chars.iter().enumerate() {
            if i == 0 || chars[i-1] == b' ' {
                let ct = [c];
                let v = from_utf8(&[c]).unwrap();
                abv.push_str(&v);
                abv.push_str(".");
                continue;
            }
        };
        abv
    }
}

In the name_abv method when using the std::str::from_utf8 if I try to pass the c byte directly from_utf8(&[c]).unwrap() the following error ocurs:

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:27:36
   |
27 |                 let v = from_utf8(&[c]).unwrap();
   |                                    ^^^          - temporary value is freed at the end of this statement
   |                                    |
   |                                    creates a temporary which is freed while still in use
28 |                 abv.push_str(&v);
   |                              -- borrow later used here
   |
   = note: consider using a `let` binding to create a longer lived value

But if I use the code exactly as it is in the method above everything works fine, the difference is that I created the variable ct let ct = &[c] and passed to from_utf8 which is basically the same thing. Can anyone help me understand this behavior ?

Thank you!!

Hi @fnalonso can you paste this into Rust Playground and send us the link?

There is no fn main() here, which makes it harder to take this for a test drive.

1 Like

The problem is that from_utf8(&[c]) creates a temporary value [c], which you then take a reference to, which is then returned from from_utf8(). The temporary value is then dropped after the expression ends, since you don't store it anywhere, leaving the reference dangling, which is unsafe so Rust doesn't allow it.

The solution is to store the temporary value somewhere and take a reference to the location. You are already storing it in ct, so you just need to change it to take a reference to that variable:

let v = from_utf8(&ct).unwrap();

You also shouldn't need to borrow again when passing v to push_str().

3 Likes

Apart from that, your code handles encodings incorrectly, and your from_utf8 will not work — passing a single byte to it is invalid for non-ASCII characters.

If you're OK with the code being ASCII-only, you can simply do abv.push(c as char).

Otherwise you'll need to use str.chars() iterator. To check the previous char, you'd need to keep previously seen char in a mutable variable, or do something clever with fold.

3 Likes

Hi, Steven.

Thank you for the link, this playground is really nice!

Here is the link with the full impl.

1 Like

Hello, @kornel

The from_utf8 was the first method I found to convert a byte to str, but it doesn't look to be the better way.

Actually, I was looking for something like the cast stated in abv.push(c as char), for now this is exactly what I need.

I'm following the 'The Rust Programming Language' and trying to grasp the concepts for each chapter and the code will present some messy implementations.

Thank you for the help!!

Hi, @jameseb7

Just got it!

I will need to practice more scenarios with ownership and borrowing in order understand it completly.

Thank you for the information.

The impossibility comes from UTF-8 itself, where bytes aren't characters. Code points can take up to 4 bytes, so when you call from_utf8 with just one byte, you may be missing the other 3.

Note that c as char is also invalid UTF-8, because again you may be missing the other 3 bytes. In this case c is interpreted as being in ISO-8859-1 encoding instead, but because the input was in UTF-8 and not the ISO codepage, the result won't even be correct for character range of ISO-8859-1.

You can use let chars: Vec<char> = str.chars().collect(); to expand UTF-8 into correct 4-byte chars. That will be correct, but inefficient since char is 4 times larger than a byte.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.