Multiple mutable references of struct fields


#1

I am simulating a n body problem. I am writing equations as general so that they could be called on any struct types which implements certain traits.

But there is a problem with mutable references to the struct fields. I am stuck with this problem for a about 15 days now. I tried different designs but couldn’t figure it out. I am close, I need your help to complete it.

I want to get a mutable and a immutable reference of different struct fields in a equation. Code is at

https://play.rust-lang.org/?gist=dafc5cb5fac16546a38721acee4f9878&version=stable&mode=debug

FYI I can’t clone the vector, it is huge size. I have many other functions which takes references of the struct,
so I can’t write functions which will give mutable reference to specific types. Please give your answers keeping this as note.


#2

This type of design is not going to work well in Rust, as you’ve noticed. Fundamentally, there’s no way to indicate on a method signature that only a subset of the struct is borrowed. Instead of exposing a bunch of getters, you may want to consider making Base a bit richer - give it methods that actually perform the semantic work, and the underlying impl can then borrow its own individual fields as it sees fit. Or change the location where the fields are stored - instead of being all on 1 trait/type, use several different types that make borrowing scenarios a bit easier.

But, if you really want a “view” of the struct’s fields like this, you can essentially ask for the underlying type to deconstruct itself and present you with all the mutable borrows via another type: play example


#4

Thanks for the reply vitalyd. I will go with the second implementation. It is simple, short and looks easy.
Will this effect any performance?

I haven’t understood much about the the first implementation, can you explain it with any simple example?


#5

Unlikely, but depends on what and how you’re doing things. The BasePartsMut struct is 96 bytes in size on 64bit - if you copy it around too much and the compiler isn’t able to optimize around it, you may pay some cost. But it’s hard to speculate. My hunch is it shouldn’t be an issue.

Edit: I should mention that you can slim BasePartsMut down to 48 bytes by returning &mut Vec<f32>s instead of slices. But I used slices because that gives your implementations more freedom for how they store the floats - anything that can be deref’d to a slice will work, not just Vec. But thought I’d clarify this.

For the example you pasted, turning body_force and spring_force into methods encapsulates all the details, and allows the underlying type to borrow its own fields freely: play

The gist is instead of asking a trait for its state piece-by-piece (i.e. getX, getY, getZ, …), you just tell it to do something at a higher level (compute body force, compute/apply spring force, etc).


#6

Alternative you can store positions, velocities and accelerations i separate structs. This allows you to update one property while keeping the others constant.

// Perhaps use a vector class from a library here
struct Vector {
    pub x: f32,
    pub y: f32,
}

fn main() {
    let mut masses = vec![ 1.0, 2.0];
    let mut positions = vec![ Vector {x:1.0, y: 2.0 }, Vector {x:3.0, y: 4.0 }];
    let mut velocities = vec![ Vector {x:1.0, y: 2.0 }, Vector {x:3.0, y: 4.0 }];
    let mut accellerations = vec![ Vector {x:1.0, y: 2.0 }, Vector {x:3.0, y: 4.0 }];
    
    update_accellerations(&masses, &positions, &mut accellerations);
    update_velocities(&accellerations, &mut velocities);
    update_positions(&velocities, &mut positions);
}

If you are doing molecular dynamics, then you could have a look at https://github.com/lumol-org/lumol.


#7

I implemented the BasePartsMut design. I have got one last problem of taking mutable references
of a vector.

https://play.rust-lang.org/?gist=af47be915d745c0efd2fb5c73fcd21e2&version=stable&mode=debug

How to solve this?

edit1: I know split_at_mut method on vector, but Is there any method which gives just the single element, rather than range?


#8

You’ll need split_at_mut here or use raw pointers + unsafe code.

The split is needed to convince the compiler that src and dst are different references - it doesn’t know that src_id and dst_id are different. So if you split and index into the two halves, that it understands.

But, if you don’t want to do split gymnastics then you can use unsafe code and put some assertions that src_id != dst_id and it’ll be safe in the end.


#9

Not particularly molecular dynamics, but I am simulating Discrete element method.

Ya I can use that, but I have too many properties for a particle, so I can’t create variables like that
for every equation and pass them.


#10

I asked in irc beginner’s, this what serk came up with.

fn get_two_mut<T: Base>(data: &mut [T], mut a: usize, mut b: usize) -> (&mut T, &mut T) {
    assert!(a < data.len() && b < data.len() && a != b);
    unsafe {
        (
            &mut *data.as_mut_ptr().offset(a as isize),
            &mut *data.as_mut_ptr().offset(b as isize)
        )
    }
}

pub fn a<T>(data: &mut [T], a: usize, b: usize) -> (&mut T, &mut T) {
    get_two_mut(data, a, b)
}

This gives an trait bound error as

the trait bound `&mut T: base::Base` is not satisfied (the trait `base::Base` is not implemented for `&mut T`)

I guess I need to implement Base for mut DemDiscrete too. Is there any other way?

Main base.rs file is at https://gitlab.com/dineshadepu/dem-rs/blob/master/src/base.rs#L4
and equation file is at https://gitlab.com/dineshadepu/dem-rs/blob/master/src/dem.rs


#11

You shouldn’t need any generic bounds (ie no T: Base) on these functions - just the generic by itself is sufficient.


#12

I just realized that I forgot to mention that get_two_mut can be written noticeably more compact and simpler:

fn get_two_mut<T>(data: &mut [T], a: usize, b: usize) -> (&mut T, &mut T) {
    assert!(a != b);
    let ptr: *mut [T] = data;
    unsafe {
        (&mut (*ptr)[a], &mut (*ptr)[b])
    }
}

This makes use of slice’s range checks, so that part of the assert isn’t needed.


#13

@vitalyd, I finally got the implementation right. Here is a sample demo Particles in vessel.

I am generalising the design for more general structs. Is the following possible

https://play.rust-lang.org/?gist=2a394a57614e6f668dc7a5d586511b56&version=stable&mode=debug

Here I want to write a function in a trait which returns different struct depending on the struct.

Thank you.


#14

Very cool!

This really begs for generic associated types (GAT) but try this in the meantime.

This will likely lead to borrow/lifetime issues, however, due to using &'a mut self in the trait methods. But, you might get lucky and sidestep it.

As mentioned upthread, you should also consider providing higher level methods, rather than exposing the “parts” to the caller. In general, the more confined you can keep the borrows, the better off you’ll be.


#15

@vitalyd This implementation doesn’t recognise the attributes in the mutable struct.

https://play.rust-lang.org/?gist=b6e4b288d07e9d85d9b04afa4bfb74e6&version=stable&mode=debug

And can I know why we are implementing a combine function?


#16

I added the combine() as a demo of how you’d make use of the returned parts generically. As it stands, there’s no unified trait that allows you to generically retrieve the bits from a given Parts associated type - each implementation of GetMut can return a different type, as you have with FooMut and BarMut.

You can try to unify them by putting the accessors on the Parts trait, like so. But this goes back to exposing internals, and sooner or later, you’ll run into borrowck problems I suspect.


#17

Hi @vitalyd. I am extending program, need some help.

In this play I am implementing an equation (Which manipulates the destination struct attributes which is acted up on by source struct).

For a general case there is a situation where destination it self could become source and should be considered as a source for the equation. In that case I have to mutably borrow such a struct twice. I don’t know how to do that ( I think I should use RC, but not sure ).

Another way of implementing such an equation is two write two functions where, If same struct is repeated twice, we adapt the function for a self influence case, and write another function where other structs influence the destination. (This is just repeating your self ). I implemented it here Play.

Is there a way, where I can combine both the cases and use a single equation?

Thank you.


#18

You can’t mutably borrow a struct if it’s also borrowed immutably or mutably - there’s no way to do that. A RefCell (likely what you’re thinking of, rather than Rc) allows dynamic borrowing, but it’ll still be enforcing the same rule: immutable XOR mutable borrows. If you were to try doing this with RefCell, the code would compile but you’d get a runtime panic.

This is probably the approach I’d look into myself. It also seems to yield more straightforward and self-evident code. Are there a bunch of such functions that you’d need to write? If so, maybe a macro can assist in generating them, or perhaps some helper functions can be created that encapsulate some parts of the two function variants.


#19

@vitalyd the function I implement inside it is huge, for example

Code has self implemetation and Code has other source implementation. And there is more physic equations, similar to this.
How can we make it general, thank you.


#20

I am going with writing both the equations as there is no other option, if any please respond, thank you.