Skip to content
Permalink
Branch: topic/visibili…
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
133 lines (96 sloc) 4.85 KB
  • Feature Name: visibility_override
  • Start Date: 2015-04-15
  • RFC PR:
  • Rust Issue:

Summary

Allow privacy violations in certain contexts when explicitly indicated to reduce the need to copy around code.

Motivation

It's often the case that you want to augment some piece of library code you didn't write yourself with minor changes. For example, you might want to re-implement a specific accessor to a container for speed reasons. Or you want to re-use a large piece of logic with some small modifications. When doing this, you inevitably run into privacy violations. See also the discussion on the Rust-internals forum: 1 and 2.

Currently, you have two options in Rust. The first is making a local copy of the library source code for your modifications and the second is copying the struct definition and doing an unsafe typecast. The reasons why these options are not sufficient are discussed in the alternatives section below.

Most, if not all, languages that implement information hiding also have some way around it through protected members with inheritance, reflection, or context switching.

Detailed design

To provide much flexibility while limiting programmer mistakes due to inadvertent privacy violations, the following change to the privacy checker is proposed:

Accessing private members of a struct X is allowed in an implementation of a trait Y for that struct X when the impl has the visibility_override attribute.

Here's an example of how this would work:

use std::intrinsics::cttz32;
use std::collections::BitVec;

pub trait LowestBit {
    fn lowest_bit(&self) -> Option<usize>;
}

#[visibility_override]
impl LowestBit for BitVec {
    fn lowest_bit(&self) -> Option<usize> {
        // Currently: field `storage` of struct `collections::bit::BitVec` is private
        for (idx, &word) in self.storage.iter().enumerate() {
            if (word==0) { continue }
            return Some(idx * 32 + (unsafe {cttz32(word) as usize}));
        }
        return None;
    }
}

Drawbacks

In general, for all the following drawbacks, keep in mind that all this this is already possible using unsafe type casts (see below), and doing that provides none of the compile-time guarantees that this implementation would provide.

Circumvents information hiding

This feature circumvents information hiding/encapsulation. This might induce programming mistakes. However, the proposed implementation should prevent accidental use and only override visibility for programmers who know what they want to do.

Unsafety

Using this feature can cause major bugs when the code you're interacting with assumes invariants that one is now able to modify and break. For example, if you change Vec.cap you can cause buffer overflows or even just reading Mutex.data might introduce data races.

Backwards compatibility

Allowing access to private members makes backwards compatibility harder, as library users may use this functionality to depend on internal private definitions. Using this attribute can be thought of as a declaration that "if upstream breaks me it's my fault," but similar situations in the past have not always worked out that way.

Alternatives

Existing alternatives

As mentioned in the motiviation, there are two existing ways to get around the privacy problem.

Making a local copy of the library source code for your modifications

While this is a safe and functional solution to the problem, it significantly burdens the developer, as they now need to distribute their own version of the library with their software, and they have to maintain this copy and keep it up-to-date.

In the case of the standard library, this might not even be possible. In that case, one might try to "rip out" the part of the library that the developer wants to modify, but this might be much larger than expected because of private dependencies and does not solve the maintenance problem.

Copying the struct definition and doing an unsafe typecast

While possible, this turns something that used to be a privacy violation into a potential memory safety error. All compile-time guarantees are thrown out of the window. The Rust Reference guide explicitly states that "Reading data from private fields" is not considered unsafe.

Alternative implementations

Disabling privacy in unsafe blocks

This seems like an overly broad solution to the problem.

Unresolved questions

Is the proposed change sufficient to cover all use cases accessing private members?

It has been suggested the attribute be called unsafe_visibility_override or similar so it shows up in greps for unsafe.

You can’t perform that action at this time.