How do I mutate a value of a struct referenced by another struct?

I'm trying to output a pcap file with truncated data using the pcap create. Trying to update the caplen field of the PacketHeader (which is a reference in the Packet struct) fails

while let Ok(mut p) = cap.next() {
        let data = p.data.iter().take(100).map(|d| d.to_owned()).collect::<Vec<_>>();
        let length = data.len() as u32;
        p.data = &data;
        p.header.caplen = length;
        savefile.write(&p);
    }
error[E0594]: cannot assign to `p.header.caplen` which is behind a `&` reference
  --> pcap-process/src/lib.rs:30:9
   |
30 |         p.header.caplen = length;
   |         ^^^^^^^^^^^^^^^^^^^^^^^^ cannot assign

How do I get the ability to mutate this referenced struct? I tried doing

let mut header = *p.header;
header.caplen = length;

But I guess this creates a copy? p.header and header have different values for caplen and the output is still the original size unless I do something like p.header = &header but copying the whole struct to change one value seems like the wrong approach.

If you have & (a shared borrow) of an existing struct that only uses simple data types like u32 or usize, then there's no way for you or anything else in your program change them for as long as the borrow exists. It's existence is what makes the struct is immutable and guarantees it cannot change. You would have to have an exclusive borrow (&mut) or own that struct (no & in the type).

If you can control what types are in the struct, then you can change the struct to make use of "interior mutability". Wrapper types like atomic integers, RefCell or Mutex can make things temporarily and selectively mutable even when they're behind a shared borrow.

1 Like

Another alternative is to use an API, such as next_mut() (if it exists), that returns an exclusive reference to the PCAP buffer. Declaring that the reference itself (e.g., a pointer) can be altered (mut p) does not give you the ability to mutate the object that is pointed to.

If that is not possible, because the PCAP API does not offer a way to give you mutable access, you can .clone() the buffer, creating a copy that you own, and then modify the copy.

Thanks, but I'm still confused. In this code, p is a Packet (not &Packet), it's only the header field that is an &PacketHeader so p is mutable, and I can change data and header. From the documentation I thought if a struct was mutable, so were its fields. I guess this doesn't apply here since the PacketHeader is only referenced and therefor not part of the struct?

Also, what is the reason *p.header seemingly makes a copy of the PacketHeader rather than just returning the struct that is referenced?

Thanks for your help.

Looks like Packet only contains a reference to a PacketHeader. You can mutate the field by replacing it with another reference like you're doing it with data, but you can't mutate the existing header through Packet.

As for the copying, PacketHeader implements Copy. If it wasn't Copy, you'd get an error saying cannot move out of `*p.header` which is behind a shared reference.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.