Members in type more accessible than the type itself

(internal) struct Foo {
    public let bar: Int
}

This is currently allowed and I don't understand why. Am I missing something? If one cannot refer to the type itself, then members cannot be accessed anyway - wouldn't it make sense to make this an error?

IIRC this is by design. I don't remember why, but I can guess that if you already providing an access modifier explicitly, it will behave as intended if you change the access level of the type to public for example.

From what I can remember, I think the design reasoning was that it helps a code author transition a type from private to internal to public. They can essentially write out what properties and methods are allowed to be public upfront before committing to actually making the type public.

If this wasn't allowed, the visibility of all the properties of a type would have to be changed as the visibility of the scope changes, causing quite a lot of code churn.

2 Likes

That seems like sacrificing clarity now for some potential benefit in the future. So far I've never wanted to do this and only encountered it in code review when a type was actually transitioning from public to internal.

This would also be a crucial feature for public subclasses with "hidden" ancestors.
We can't do that now, but especially since we also don't have abstract classes, I think this would be a valuable addition.

I believe it's useful in some situations.

For example, sometimes you want to expose part of your private type's APIs to public, through protocols.

public protocol Nameable {
  var name: String { get }
}

private struct A {
  public name: String
}

extension A: Nameable { }

public func getSomeNameable() -> Nameable {
  return A(name: "123")
}

I'm not sure what you wanted to showcase with this example, it's totally valid if the property remains private or interval.

This compiles just fine.

public protocol Nameable {
  var name: String { get }
}

private struct A {
  var name: String
}

extension A: Nameable { }

public func getSomeNameable() -> Nameable {
  return A(name: "123")
}

I thought sjavora just wanted to know when a member can have a higher accessibility level than its enclosing type...

And what you pointed out to me is also true, except that private is not an option while internal is OK, I wonder what I have missed.:thinking:

This is the officially quoted reason.

The more pragmatic reason that prompted this design in the first place is that, for a private type, it is impossible to spell the same level of access for its own members, because private inside a scope is less than private of the containing scope.

private /* == fileprivate */ struct A {
  fileprivate let x: Int = 42
  private /* < fileprivate */ struct B {
    /* ??? */ let y: Int = 42
  }
}
5 Likes