Trait Objects and BorrowMut


#1

Hello Rustaceans!

In the following code, variant A compiles fine, but variant B yields error: `r` does not live long enough. The only difference is that variant A uses the concrete type u8 while variant B uses a trait object of type T. I don’t understand how this influences the borrow checker.

use std::borrow::BorrowMut;

trait T {}

impl T for u8 {}

fn main() {
    // A:
    {
        let mut r: Box<u8> = Box::new(23u8);
        let _: &mut u8 = r.borrow_mut();
    }

    // B:
    {
        let mut r: Box<T> = Box::new(23u8);
        let _: &mut T = r.borrow_mut();
    }
}

#2

I don’t really understand it, but the following works:

    {
        let mut r: Box<T> = Box::new(23u8);
        let _: &mut T = &mut *r;
    }

It seems that it has something to do with the BorrowMut trait.


#3

Hi troplin! Thanks for the hint. This solves my code problem!

I would still really like to understand the bug in my original example, since it indicates that my mental model of how borrow checking works is wrong.


#4

I’m a Rust newbie myself, but this is indeed a bit of a head scratcher. Even if you put the _ binding into its own smaller lexical scope (i.e. put another set of braces around it), the compiler still complains for the same reason. If you change the binding to be non mut, then it compiles.

Having said that, is AsMut more appropriate here though? Borrow/BorrowMut, I think, are intended for slightly different scenarios, such as different types of borrows (the canonical example being a Vec borrowed as a slice [T]). Here you’re trying to just borrow the trait object as a “normal” mut borrow. Maybe I’m wrong though - would be interested to hear an explanation myself.


#5

Hi vitalyd! As I’m a Rust newbie myself I’m not yet clear how Borrow (BorrowMut) differs from AsRef (AsMut). That said, I just tried it with all four Traits. Non mutable references using AsRef and Borrow compile, while AsMut behaves like BorrowMut in the original example, i.e., rustc complains about the livetime of r.


#6

I think that both AsMut/AsRef and Borrow/BorrowMut are mainly used in generic function signatures.
If you just want to borrow something, just use the borrowing operator (&).

There’s also a chapter in the Rust book about those traits:
https://doc.rust-lang.org/stable/book/borrow-and-asref.html


#7

Interesting - I could’ve sworn r.as_mut() (instead of r.borrow_mut()) compiled for me, but perhaps I’m wrong. It would be a bit surprising given their signatures appear the same.


#8

Yeah I agree. I think this is mostly a “what exactly is the borrow checker complaining about”, rather than what you’d normally write. For some reason, it thinks ‘r’ is still borrowed even though it doesn’t look like it. So I’d be curious to know the reason as well, even if this isn’t the type of code you’d typically write in this situation.


#9

I tried comparing the current rustc nightly with rustc 1.14.0 and yes, the current nightly shows no error using r.as_mut() while 1.14.0 does. Both versions show the error message when using r.borrow_mut(). So the behavior for the current nightly is different for two functions with the exact same type signature. This means that either its not possible to reason about lifetimes using only the types of all used symbols (but I suspect this is a goal of rust) or there is a bug in the current nightly.


#10

IMO both should work. borrow_mut() and as_mut() both have the same signature (and are implemented the same way for Box<T>), it should not be possible that they behave differently.
It’s also strange that it works if mutability is removed everywhere (i.e. use borrow instead of borrow_mut).

I think you should file an issue.
My guess is, that this is just an ugly side effect from Box being special cased in the compiler.


#11

I filed an issue: https://github.com/rust-lang/rust/issues/38624