How to implement readonly struct properly?

I want a struct A with two fileds b and c which are readonly, I can make b and c private and provide getters to read them: b(self) -> B and c(self) -> C, but I can't read both at the same time. However, if I make them public and read them directly, rust compiler is smart enough to know that it's safe and it compiles.
So my question is that is there a way to implement readonly struct without losing the ability to read multiple fields?

The trick is to make the “getters” operate with immutable references instead of by-value.

The type signature pub fn b(self) -> String as in the example actually states “give me the whole value of type A and I will give you a single String, discarding anything else inside the A struct that didn’t make it into the return value”.

Modifying the playground accordingly would give:

If you also want a method to give ownership of the fields, you could provide a single method that returns all the fields at once, e.g. in a (String, String) tuple, or in a convenience struct with more properly named public fields.

Also note, that the &String type in idiomatic Rust is usually replaced with &str; though in a return value that’s not all that important, this modification would look like so: Rust Playground


Finally, as you seem to be fairly unfamiliar with Rust’s ownership and borrowing system so far, you might be basing your initial goal of “I want a struct with read-only fields” on incorrect assumptions. In many other programming languages, defining something like a “struct” or maybe “class” with fields that are not protected to be read-only by hiding them behind getters, will immediately result in multiple copies of (references to) your object sharing mutable state. Rust works differently. You will never accidentally run into shared mutable state (in fact, it can be quite tedious to get any shared mutable state in the first place in the cases where you might actually need it), so the main motivation for disallowing mutation is gone.

There are remaining cases where preventing mutation of fields is sensible though. This mainly happens if the struct upholds some kind of invariant/property of the data contained within it which is checked on construction and relied upon in its methods/API. If your struct does have such checked invariants, then hiding the fields behind read-only getters can be sensible; but if it’s a straightforward data-type which publicly exposes all contained fields, and does no validation on construction, then there’s usually no good reason at all why you’d want to prevent mutation in the first place.

6 Likes

Thanks for your quick reply! I'm familar with rust' ownership and borrowing system. From your explaination I notice that my data structure design is problematic, I'll rethink it.

FYI, the pattern below may be useful for someone.

// public plain old data type
struct A {
  pub b: B,
  pub c: C,
}
struct B;
struct C;
// newtype pattern
struct D(A);
impl D {
  pub fn mut_keep_invariant(&mut self) {}
  pub fn b(&self) -> &B { &self.0.b }
  pub fn c(&self) -> &C { &self.0.c }
  // transfer the ownership of all fields
  pub fn to_mut(self) -> A { self.0 }
}
1 Like

Steffahn has answered it already. Just to summaries.
Reading a value or mutating it or taking it , all three are different things.
In case you have a set of items which you only want to read from , then I would say tuple could be a good choice

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.