最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

rust - How to instantiate a struct containing many private properties in a separate module without using a constructor function?

programmeradmin1浏览0评论

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 0
Add a comment  | 

2 Answers 2

Reset to default 1

Public 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.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论