Newbie--implement clone for &mut B?

I have:

#[derive(Copy, Clone)]
struct B {
data: [u8; 1000],
}

#[derive(Clone)]
struct Info<'a> {
b: &'a mut B,
}

I get the following error:

error[E0277]: the trait bound &mut B: std::clone::Clone is not satisfied
--> src/lib.rs:8:5
|
8 | b: &'a mut B,
| ^^^^^^^^^^^^^^^^^ the trait std::clone::Clone is not implemented for &mut B
|
= help: the following implementations were found:

= note: std::clone::Clone is implemented for &B, but not for &mut B
= note: required by std::clone::Clone::clone

I've used C almost exclusively for decades, so I'm not surprised if I'm misinterpreting what I need to do. What I'm trying to do is to have Info variables have reference to a static array of Bs. Do I need to do this with pointers?

It's not possible to do this, at least in the way you've formulated it here.

If you think about it, an &mut B is a unique reference to some B which is located elsewhere in memory. If you were able to implement Clone for Info that'd mean you get a second unique reference to the same B... Which violates the "unique" part in unique reference.

There's no need to reach for unsafe and raw pointers here, there are usually several other ways to structure your code which are idiomatic and normally a lot cleaner.

I guess I'll start by asking if you really need to implement Clone for Info? Most types which contain a &mut B tend to be short lived and mediate the access to the underlying B (e.g. by providing helper methods for mutating it, or by guaranteeing safety invariants like RefCell).

Another question to ask is whether Info needs to mutate the B it points to? If you're just providing getters which read fields out of some packet, you can use an &B.

This isn't necessarily relevant to your problem, but what made you choose a [u8; 1000] instead of Vec<u8>? That 1000 constant feels a bit arbitrary, so I'm guessing you're keeping track of the number of bytes which are logically used elsewhere. If that's the case, you can use Vec<u8> to automatically keep track of things like length and it'll handle resizing when you add more than 1000 elements to your array.

2 Likes

If you're a beginner, you need to avoid putting temporary references in structs. It doesn't do what you expect.

Rust references are not pointers. They are read-write locks, and you can't clone an exclusive lock, because then the lock wouldn't be exclusive anymore.

In this case if you want Info to be cloneable, then make it contain Rc<RefCell<B>> or the same but thread-safe Arc<Mutex<B>>. This changes compile-time &mut lock to run-time Mutex lock.

If you don't need to mutate B, then &B is cloneable, because it's OK to have multiple shared locks. But you'll want Rc<B> if you want to return the struct from a function or move it around. Structs with temporary references inside will be temporary themselves and won't be allowed to be used outside of their scope.

7 Likes

I'm doing bare metal code for an embedded application. This prevents me, at this stage of the application, from using Vec. I don't really want to implement Clone for Info; the compiler suggested I needed it for the larger program from which this came.

The idea of not needing to mutate B is interesting. There are two phases, one in which B is an initial database and doesn't need to be mutated, and the second phase where it does. I can probably redesign things to use two different Infos, one for each phase.

1 Like

Actually, I was surprised it didn't complain about the references but maybe it would have the compiler finished that phase. As I mentioned in the reply to @Michael-F-Bryan, using Rc is not possible because I don't have a memory allocator at this point. You both suggest making B non-mutable, which I think is a workable solution.

Note that if the lifetimes check out, you can use &RefCell<T> instead of Rc, or if T is Copy, you can even use &Cell<T>.

Both of these opt-in to shared mutability by sacrificing the ability to use it from multiple threads simultaneously.

1 Like

One thing you can do with a mutable reference is reborrowing it, maybe it helps:

struct Info<'a> {
    b: &'a mut B,
}

impl<'a> Info<'a> {
    pub
    fn reborrow (self: &'_ mut Info<'a>)
      -> Info<'_>
    {
        Info { b: &mut *self.b }
    }
}

If this is for an embedded device I'm guessing these will be "true" static mut variables. You'd need to be careful using RefCell because static variables are commonly used to communicate between an interrupt and the rest of the program, and if an interrupt fires at the wrong time you could get a panic (interrupt tries to borrow mutably, but the main code already has a mutable borrow) or data race (interrupted while fiddling with the RefCell's bookkeeping).

2 Likes

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