Accessing private struct members

I ran across this and found it strange. That I can impl a type defined in the same crate in a different file but I cannot access private fields (in an impl or a trait impl for that type). I know that it is possible to change the “public” nature of a struct (for instance using pub(super)) but that makes the private fields accessible in that place for all functions, not just impl’s.

Maybe I don’t quite understand what rust’s meaning of private/public is. But after searching around I could neither find that answer or find an answer as to why this was the way it was.

Proposal: Private member fields should be accessible in impl blocks associated with that type.

This maybe belongs on the user forum, not the internals forum.

“Private” means “visible within the same module, or sub-modules”. If you want to access private fields in an implementation, put it in either the same module or a sub-module.

Maybe it does. But perhaps there should be a way to say “visible in any impl”

But that would just be equivalent to “public”, since it’d allow anyone to access the internals of a type from anywhere. If you mean “within a crate”, that’s what pub(crate) is for. Or, as I noted, you can just move where the impl is.

3 Likes

Accessing private struct fields breaks abstractions, which can be very harmful since it can invalidate the invariants / properties of the struct. If the struct relies on any of these invariants while doing unsafe this would mean that breaking them would lead to serious bugs, most probably UB.

For this reason it is much easier to reason about struct members as being “as private” as they are declared to be. Non-locality of properties / invariants is just a footgun whn multiple people work on the same project.

Finally, downstream crates can still impl their own traits, and there would then be definitely no abstraction whatsoever if such impls allowed to access private struct members.

Regarding your example, you can:

  • move the impls into the module (or one of its submodules) containing the struct definition;

  • define the struct members as pub(in path::to::the::other::mod) (ideally: pub(in super) and no further); you can use pub(in crate) if you don’t care that much about scalable maintainability of your project (e.g., when quick prototyping);

  • define constructors / setters / getters so that you can access the struct members through them;

    • (if the public ones are not flexible enough, remember you can define pub(in path) associated functions and methods too)
2 Likes

Oftentimes I use privacy to make it easier to reason about the correctness of unsafe code. I might make a small module containing some private things and put only the minimal public API in there to safely wrap the unsafety.

If private fields could be accessed in impls anywhere, then the purpose of these modules would be defeated and I would have to audit the rest of the crate for access to private members.

(This also applies to safe code for maintaining invariants which are difficult to reason about)

4 Likes

What about introducing a pub(in impl) visbility?

I think that we just went through a bunch of module/path/vis changes with Rust 2018 and we should let that rest for a longer period of time before we start making large changes like pub(in impl) to our module system. The module system is already pretty complicated as well so more conceptual complexity may be more than we can handle.

5 Likes

I question whether C++ protected was a good idea in the first place, especially in the absence of data inheritance. It sounds like this is what that is.

6 Likes

I think the original question comes from a mindset that is more OOP oriented than Rust. When you look at struct + impl as an „object“, then it makes sense its methods should be able to look inside it.

On the other hand, if you look at it from the point impl is just something like a mod/namespace with some integration with the . sugar, then it’s natural that impl in another module doesn’t and shouldn’t see the private stuff. After all, any other function in there doesn’t see it either.

5 Likes

Maybe that was it. But also I think that it helps with separation where the type definitions are in one file and the impl’s are in another. Also @ExpHP, I don’t quite see how this would break correctness reasoning. Since this wasn’t for all impl’s just the impl’s for that type. Also, it should still be restricted by the “only impl for crate local types” requirement.

If you want this, and you don't want to stick to rust's directory structure, you can declare impls in whatever other file and then include them in a submodule of the definition file with

#[path = "path/to/impls.rs"]
mod impls;

To be clear, this is guaranteed to draw funny looks. Just declare impls in the same file as the types.

1 Like

So why not just use pub(crate) on fields of your struct?

Even if I split impls into a separate type (which is quite rare), they usually reside in submodules of a module in which this type is defined, so I don’t quite understand your use-case.

1 Like

I have path/types.rs and path/impls.rs. Which seems to have been my problem. However, I think that the rust book could do a much better job at explaining the differences between pub/priv and what is accessible where.

Usually type definitions are small enough to be kept inside mod.rs. As for book, I think PRs are welcomed. :slight_smile:

1 Like

Any other module could define an impl for that type.

1 Like

Just imagine writing impl MyTrait for Vec<T> and setting self.len = usize::max_value(). All safe code, no eyebrows raised by rustc.

As others have said, reasoning about unsafe requires careful walling off of implementation details, and the privacy system is one way Rust lets you do that.

8 Likes
pub // The struct is public
struct Foo {
    pub(in crate::path::impls) // the field is only visible for the impls (and current module)
    some_field: SomeType,
}
1 Like

I don't think they're talking about traits, they're talking about regular impl, which is allowed to be spread across multiple files within the same crate.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.