I want the user to be able to pass the struct along and instantiate it without a constructor function, but not be able to modify the structure's fields without using the setter methods provided in the implementation. Is this possible in Rust without wrapping the structure in a immutable structure such as Arc/Rc?
I don't want to use a constructor method because, if the struct that is being constructed has many fields, it seems much cleaner to me (and the rustfmt clippy linter) to be able to see what values are being set to specific struct fields upon instantiation. It also prevents typing the arguments out of order which could be a much bigger issue if you have 2 of the same parameter type next to each other in the arguments.
I would also prefer not to use pub(crate)
because that still leaves the
possibility of a maintainer accessing the field outside of an implementation
method.
mod foo {
pub struct Foo { // Struct is public
bar: usize // Field is private.
}
impl Foo {
// This is the only way I want to allow the user to modify `bar`. I
// explicitly do not want to allow them to directly access struct
// fields such as `let baz = foo.bar` or `foo.bar = 1` from down below.
pub fn set_bar(&mut self, bar: usize) {
self.bar = bar;
}
}
}
fn main() {
let mut foo = foo::Foo {
bar: 0 // ! Compilation fails here because this field is private.
};
// I want this to continue to fail to compile.
foo.bar = 1;
// But I want this to succeed.
foo.set_bar(1)
}
Apologies if this is a duplicate I looked for constructor instantiation questions related to private fields and couldn't find one that dealt with this specifically. The closest is this post by Alika97 but didn't necessarily deal with a large number of fields (and therefore function parameters).
If constructing a public struct with private fields outside of the module isn't possible/feasible what is a better solution than a constructor with many parameters, or is that my only course of action if I don't want the fields to be public (even within the crate)?
I want the user to be able to pass the struct along and instantiate it without a constructor function, but not be able to modify the structure's fields without using the setter methods provided in the implementation. Is this possible in Rust without wrapping the structure in a immutable structure such as Arc/Rc?
I don't want to use a constructor method because, if the struct that is being constructed has many fields, it seems much cleaner to me (and the rustfmt clippy linter) to be able to see what values are being set to specific struct fields upon instantiation. It also prevents typing the arguments out of order which could be a much bigger issue if you have 2 of the same parameter type next to each other in the arguments.
I would also prefer not to use pub(crate)
because that still leaves the
possibility of a maintainer accessing the field outside of an implementation
method.
mod foo {
pub struct Foo { // Struct is public
bar: usize // Field is private.
}
impl Foo {
// This is the only way I want to allow the user to modify `bar`. I
// explicitly do not want to allow them to directly access struct
// fields such as `let baz = foo.bar` or `foo.bar = 1` from down below.
pub fn set_bar(&mut self, bar: usize) {
self.bar = bar;
}
}
}
fn main() {
let mut foo = foo::Foo {
bar: 0 // ! Compilation fails here because this field is private.
};
// I want this to continue to fail to compile.
foo.bar = 1;
// But I want this to succeed.
foo.set_bar(1)
}
Apologies if this is a duplicate I looked for constructor instantiation questions related to private fields and couldn't find one that dealt with this specifically. The closest is this post by Alika97 but didn't necessarily deal with a large number of fields (and therefore function parameters).
If constructing a public struct with private fields outside of the module isn't possible/feasible what is a better solution than a constructor with many parameters, or is that my only course of action if I don't want the fields to be public (even within the crate)?
Share Improve this question asked Mar 28 at 15:48 GrimOutlookGrimOutlook 331 silver badge2 bronze badges 02 Answers
Reset to default 1Public struct with private fields is exactly the pattern we use to *disallow* instantiating such a structures outside the module. However you could have a thin (or even #[repr(transparent)]
) wrapper type such as pub struct FooWrapper(Foo);
around fully public Foo
, and have a constructor taking Foo
that you can instantiate in the way you want. I am not sure if that is what was meant by "structure such as Arc/Rc", but here is an example code:
mod foo {
pub struct Foo { // Struct is public
pub bar: usize // Field is public.
}
pub struct FooWrapper(Foo /* private */);
impl FooWrapper {
pub fn new(f: Foo) -> FooWrapper {
Self(f)
}
pub fn set_bar(&mut self, bar: usize) {
self.0.bar = bar;
}
}
}
fn main() {
let mut foo_wrap = foo::FooWrapper::new( foo::Foo {
bar: 0
});
// This will fail:
// foo_wrap.0 = foo::Foo { bar :1 }
//This will fail too:
//foo_wrap.0.bar = 1;
// This works:
foo_wrap.set_bar(1)
}
Playground link
Although not a solution for today (yet), there is an accepted RFC to allow this: https://rust-lang.github.io/rfcs/3323-restrictions.html. The proposed syntax is pub(crate) mut(self) field: Type
.