Borrowing on only one Match Case

The following code does not compile:

use std::borrow::Cow;

fn get_cow(vec: &mut Vec<u8>) -> Cow<str> {
    match std::str::from_utf8(&vec[..]) {
        Ok(v) => Cow::Borrowed(v),
        _ => {
            vec.push(0);
            Cow::Owned(String::new())
        }
    }
}

fn main() {
    println!("{:?}", get_cow(&mut Vec::new()));
}

and the compiler returns the following error message:

error[E0502]: cannot borrow `*vec` as mutable because it is also borrowed as immutable
 --> src/main.rs:7:13
  |
3 | fn get_cow(vec: &mut Vec<u8>) -> Cow<str> {
  |                 - let's call the lifetime of this reference `'1`
4 |     match std::str::from_utf8(&vec[..]) {
  |                                --- immutable borrow occurs here
5 |         Ok(v) => Cow::Borrowed(v),
  |                  ---------------- returning this value requires that `*vec` is borrowed for `'1`
6 |         _ => {
7 |             vec.push(0);
  |             ^^^^^^^^^^^ mutable borrow occurs here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

Since on the Ok branch, we exit immediately, why does the borrow checker care that we are mutably borrowing on the Err branch? Is there an alternative way of writing this code which has the same functionality?

Here's the link to the playground: Rust Playground.

Your code seems sound, in principle, as demonstrated by the fact that the new in-development borrow checker polonius accepts it just fine.

The problem that the current borrow checker sees is that since you return v in one branch of the match, it things that v must live until the end of the function call, hence the vec must remain borrowed immutably for just as long.

Talking about workarounds, I’m not quite sure what the best approach is, yet, if you want to avoid unsafe code and checking the validity twice; I’ll update my answer if I find a decent approach I can’t think of a good way. I guess, you’ll just want to use the unsafe approach, because doing the conversion twice seems stupid.

Edit: For clarity, these approaches work but require unsafe or do the work twice:

fn get_cow_expensive(vec: &mut Vec<u8>) -> Cow<str> {
    if std::str::from_utf8(&vec[..]).is_ok() {
        Cow::Borrowed(std::str::from_utf8(&vec[..]).unwrap())
    } else {
        vec.push(0);
        Cow::Owned(String::new())
    }
}

fn get_cow_with_unsafe(vec: &mut Vec<u8>) -> Cow<str> {
    if std::str::from_utf8(&vec[..]).is_ok() {
        Cow::Borrowed(unsafe {std::str::from_utf8_unchecked(&vec[..])})
    } else {
        vec.push(0);
        Cow::Owned(String::new())
    }
}
2 Likes

Ok, great, that makes sense. Thank you so much for your quick response. For now, in my real application, I’ll return a owned or a reference-counted string, and I’ll move to borrowed later once polonius is integrated.