What is the "Rust way" to handle collections of structs with many fields?

Apologies for the cryptic title, I'm not sure how to succinctly describe my problem. Also keep in mind that the details here have been simplified from my actual code.

Suppose I have a struct X:

struct X {
    datum: i32,
    attribute: Option<i32>,
}

I also have a function f that gets a &mut [X] where all the attribute fields are None and computes for each X the value of attribute using function fn g(datum: i32, xs: &[X]) -> i32 (g only uses the datum field, this is important later). I would like to write the following code:

fn f(xs: &mut [X]) {
    for x in xs {
        x.attribute = Some(g(x.datum, xs));
    }
}

But that runs afoul of the borrow checker, because *xs is borrowed mutably in the for loop and again immutably in the call to g. I see two ways to solve this:

  1. Loop over indices instead, i.e.
fn f(xs: &mut [X]) {
    for i in 0..xs.len() {
        xs[i].attribute = Some(g(xs[i].datum, xs));
    }
}
  1. Split X in half, let f and g take a slice of i32 instead of X and make f build and return a vector with the attribute values.

Both solutions seem pretty inelegant to me: solution 1 has a lot of indexing and probably ends up checking the slice bounds in every iteration of the loop, while solution 2 is less ergonomic for users of the result and also loses the guarantee that the returned vector is the same length as the initial slice. My question is, assuming there's no better solution that I didn't think of, which one is considered more idiomatic in Rust? Is there even consensus about this matter?

This would probably work:

fn f(xs: &mut [X]) {
    for x in xs {
        let result = g(x.datum, xs);
        x.attribute = Some(result);
    }
}
1 Like

It doesn't; the problem is not that x is borrowed twice but that xs is already borrowed by the for loop.

@ytausky the problem is fundamental: you are not allowed to have two mutable borrowers (or a mutable and 1 or more immutable borrowers) to the same slice. As for an immediate solution, this might work (Playground):

fn f(xs: &mut [X]) {
    for i in 0..xs.len() {
        let result = g(xs[i].datum, xs);
        xs[i].attribute = Some(result);
    }
}

Ah, yeah, I didn't look closely enough.

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.