In C++, a class can be created, and mutable methods on the self can be called in the Initialization function:
class App {
private:
int foo;
float bar;
void set_bar() {
bar = (int)foo;
}
public:
App() {
foo = 0.0;
set_bar();
}
}
I understand that this can be done with options, and getter functions, but I am just wondering if there is a way to do this in rust without having to unwrap every time I want to access a member.
In Rust, the need for this isn’t all that common. Really, methods on a type usually are only used with fully constructed instances of that type. A method like set_bar() above, is that only used during construction? If so, it can easily work without actually needing a complete (but not yet fully “constructed”) value of type App… just return the field that’s computed – or if multiple values are involved, bundle them up with some tuple or other type.
Or is set_bar used both during construction, but also later during normal usage? Perhaps e.g. like some kind of (helper) method that sets up or restores an invariant? Yeah, I guess then it can make sense to fill the struct with some “dummy” data, and then call that same method afterwards.
If you want to avoid changing a field type to Option then … in your case, for a float, you can just initialize with some “garbage” like 0 I guess.
For a version of “Option” that’s really almost always not None, I guess one could define a type with an implicit unwrap in a Deref[/Mut] impl? This should avoid most of the annoyance around “too much unwrap required”; but I don’t think the need should come up all that often.
(I do however still wonder if such a type is already present in any common library or so, perhaps someone can link something interesting.)
But let’s get back to exploring the more Rusty way of constructing data: compute field values in helper functions if necessary and just pass them by value; use modules for privacy/visibility, so your free-standing helper functions needn’t be public anyway; then your example might turn out looking something like this:
pub mod app {
pub struct App {
foo: f32,
bar: i32,
}
// this could take `foo` by-reference if it was
// a more complex / less trivially copyable type ;-)
fn compute_bar(foo: f32) -> i32 {
foo as i32
}
impl App {
pub fn new() -> Self {
let foo = 0.0;
let bar = compute_bar(foo);
Self { foo, bar }
}
}
}
No, you can't have uninitialised fields without either safe wrapper like Option or unsafe one like MaybeUninit. Structs can exist in a partially moved-out-of state, but that's only for destructuring and doesn't allow method calls.
Rust usually solves this using a builder pattern — a version of the struct with optional fields that gets built and moved into a struct with all required fields. There are crates that help with the boilerplate: