# Access control for enum case initializers

**URL:** <https://forums.swift.org/t/access-control-for-enum-case-initializers/22663>\
**Category:** Pitches\
**Created:** [April 5, 2019, 12:46am UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663 "2019-04-05T00:46:54Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [April 5, 2019, 8:57am UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/21 "2019-04-05T08:57:07Z")

</div>

> [@cukr](#):
>
> I think that having two attributes that behave exactly the same, just with different messages is dumb.

Can you explain what you mean by 'two attributes' here?

* * *

Also I see your examples equivalent to:

```swift
enum MyCoolEnum {
    private(init) case privateCase
    case publicCase
}

let foo = MyCoolEnum.publicCase
if foo == .privateCase { // this will raise an error
} else if foo == .publicCase {}

```

---

<div class="post-metadata">

**Author:** ![cukr](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cukr/32/889_2.png) [@cukr](https://forums.swift.org/u/cukr)\
**Post date:** [April 5, 2019, 8:59am UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/22 "2019-04-05T08:59:56Z")

</div>

`internal(init) case x` (or `internal(call)` )and the future direction `internal case x`  
You haven't shown me any example where these two will differ

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [April 5, 2019, 9:01am UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/23 "2019-04-05T09:01:48Z")

</div>

Well this was never on the table. It's not fair calling this 'dumb' as the problem already exists today with properties that can be either just `internal` or `internal(set)`.

---

<div class="post-metadata">

**Author:** ![cukr](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cukr/32/889_2.png) [@cukr](https://forums.swift.org/u/cukr)\
**Post date:** [April 5, 2019, 9:05am UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/24 "2019-04-05T09:05:58Z")

</div>

I can show you an example where the behavior of `private` and `private(set)` is different

```swift
struct Foo {
    private var a = 123
    private(set) var b = 123
}
var foo = Foo()
print(foo.a) // errors out
print(foo.b) // doesn't error out

```

Can you show me an example where `internal(init) case x` and the future direction `internal case x` behave differently?

---

<div class="post-metadata">

**Author:** ![cukr](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cukr/32/889_2.png) [@cukr](https://forums.swift.org/u/cukr)\
**Post date:** [April 5, 2019, 9:09am UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/25 "2019-04-05T09:09:16Z")

</div>

Sorry, I automatically read `whatever(set)` as `private(set)`

I think that adding `internal` and `internal(set)` as standalone features would be dumb. But `internal` and `public internal(set)` make sense.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [April 5, 2019, 9:10am UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/26 "2019-04-05T09:10:04Z")

</div>

Sure.

```swift
// Module X
public enum MyCoolEnum {
  case a
  internal(init) case b
  internal case c

  public static var indirectB: MyCoolEnum {
    return .b
  }

  public static var indirectC: MyCoolEnum {
    return .c
  }
}  

// Module Y
import X
// ASTPrinter will give you this.
// public MyCoolEnum {
// case a
// internal(init) case b
//
// public static var indirectB: MyCoolEnum { get }
// public static var indirectC: MyCoolEnum { get }
// }

// `case c` is not exposed at all, while `case b` is but is restricted
// so that the user of module X cannot initialize it directly.

```

---

<div class="post-metadata">

**Author:** ![cukr](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cukr/32/889_2.png) [@cukr](https://forums.swift.org/u/cukr)\
**Post date:** [April 5, 2019, 9:20am UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/27 "2019-04-05T09:20:02Z")

</div>

Thank you :) I interpreted the pitch as less restrictive.

---

<div class="post-metadata">

**Author:** ![DeFrenZ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/defrenz/32/827_2.png) [@DeFrenZ](https://forums.swift.org/u/DeFrenZ)\
**Post date:** [April 5, 2019, 9:24am UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/28 "2019-04-05T09:24:56Z")

</div>

I wouldn't have been in favour for this before, but after [SE-0192](https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md) I am. Some prerequisites I feel should be there:

- frozen `enum`s cannot specify access control for `case`s different than the `enum` itself
- when `switch`ing from a context where all `case`s are accessible then `switch` behaves as for normal `enum`s requiring covering all cases
- when `switch`ing from a context where not all `case`s are accessible then `switch` behaves as with non-frozen `enum`s and `@unknown default` is needed

If these are satisfied I'd be happy with the feature 🙂

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [April 5, 2019, 9:30am UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/29 "2019-04-05T09:30:09Z")

</div>

> [@DeFrenZ](#):
>
> frozen `enum` s cannot specify access control for `case` s different than the `enum` itself

Wouldn't that mean that if you had previously a non-frozen enum with private cases (on apple platforms) that would become `frozen` you would need to expose all the private cases? Since the frozen attribute is not yet allowed for library developers to be used I'm not sure what to think about this situation.

@jrose do you have a clearer vision of this?

---

<div class="post-metadata">

**Author:** ![DeFrenZ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/defrenz/32/827_2.png) [@DeFrenZ](https://forums.swift.org/u/DeFrenZ)\
**Post date:** [April 5, 2019, 9:34am UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/30 "2019-04-05T09:34:53Z")

</div>

> [@DevAndArtist](#):
>
> Wouldn't that mean that if you had previously a non-frozen enum with private cases (on apple platforms) that would become `frozen` you would need to expose all the private cases?

Is that not already the case? If they are frozen then you don't need `@unknown default:`, which means you have all the `case`s available to `switch` on

---

<div class="post-metadata">

**Author:** ![anandabits](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/anandabits/32/355_2.png) [@anandabits](https://forums.swift.org/u/anandabits)\
**Post date:** [April 5, 2019, 2:33pm UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/31 "2019-04-05T14:33:14Z")

</div>

> [@DevAndArtist](#):
>
> Can you explain why there is a conflict? I think if you would implement the protocol in the type declaring body even your `private(init)` would not prevent you from generating the case for the collection.

It's a _semantic_ conflict. `private(init)` prevents code outside the declaring file from creating an instance. But that is defeated if the compiler makes a value of that case available via `allCases`. If the compiler does synthesize `CaseIterable` for an enum with a case initializer less visible than the type at minimum there should be a diagnostic about this. `private(init)` would be pretty pointless. It would even be possible to write a static computed property that looks just like the case initializer in an extension.

---

<div class="post-metadata">

**Author:** ![anandabits](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/anandabits/32/355_2.png) [@anandabits](https://forums.swift.org/u/anandabits)\
**Post date:** [April 5, 2019, 2:36pm UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/32 "2019-04-05T14:36:29Z")

</div>

> [@cukr](#):
>
> Would you be able to obtain the private(init) case in this way? If not, which part would be forbidden?

I don't fully understand what you are asking here, but it would absolutely be possible for values of the case to be vended by code that can see the case initializer to code that cannot see the case initializer. The pattern is still visible for matching, etc. All this does is hide the case initialize so a value of the case cannot be _created_ by code that cannot see the case initializer.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [April 5, 2019, 2:36pm UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/33 "2019-04-05T14:36:44Z")

</div>

I'm not sure I follow this. From my point of view it's exactly what `private(init)` would cause.

```swift
enum E {
  private(init) case a
  
  static var indirectA: E {
    return .a
  }
}

```

This is valid so why can't you still have `CaseIterable` directly sitting near `E`?

---

<div class="post-metadata">

**Author:** ![anandabits](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/anandabits/32/355_2.png) [@anandabits](https://forums.swift.org/u/anandabits)\
**Post date:** [April 5, 2019, 2:37pm UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/34 "2019-04-05T14:37:44Z")

</div>

> [@cukr](#):
>
> What does pattern matching means in swift if not `switch` ?

Pattern matching also includes `if case` and `guard case`.

---

<div class="post-metadata">

**Author:** ![anandabits](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/anandabits/32/355_2.png) [@anandabits](https://forums.swift.org/u/anandabits)\
**Post date:** [April 5, 2019, 2:47pm UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/35 "2019-04-05T14:47:57Z")

</div>

> [@cukr](#):
>
> I'll ask again. Would this be possible?  
> ...  
> How about this?

Yes, the case is visible for the purpose of pattern matching. That is why I am pitching the parameterized access control instead of applying access control to the case across the board. Access control for the purpose of pattern matching is much different in scope and semantics.

> [@DevAndArtist](#):
>
> Also I see your examples equivalent to:
> 
> ```swift
> enum MyCoolEnum {
> private(init) case privateCase
> case publicCase
> }
> 
> let foo = MyCoolEnum.publicCase
> if foo == .privateCase { // this will raise an error
> } else if foo == .publicCase {}
> 
> ```

This would indeed raise an error. In order to compare equality you need to be able to construct an instance using the case initializer, which is not visible to the code in the example.

> [@cukr](#):
>
> `internal(init) case x` (or `internal(call)` )and the future direction `internal case x`  
> You haven't shown me any example where these two will differ

I hope I have answered that question in this post. Code that cannot see the case initializer is able to pattern match but cannot use the case initializer to make a value. The future direction would disallow both pattern matching _and_ value initialization. This means that exhaustiveness analysis would need to consider access control. That is a much larger change than what I am pitching.

> [@DeFrenZ](#):
>
> I wouldn't have been in favour for this before, but after [SE-0192](https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md) I am. Some prerequisites I feel should be there:
> 
> - frozen `enum` s cannot specify access control for `case` s different than the `enum` itself
> - when `switch` ing from a context where all `case` s are accessible then `switch` behaves as for normal `enum` s requiring covering all cases
> - when `switch` ing from a context where not all `case` s are accessible then `switch` behaves as with non-frozen `enum` s and `@unknown default` is needed
> 
> If these are satisfied I'd be happy with the feature 🙂

As noted above, this pitch _does not_ change the behavior of `switch` or pattern matching at all. That is why the access modifier must be parameterized (i.e. `private(init)` instead of just `private`). The _future direction_ of a non-parameterized access modifier would require code that can't see the pattern for every case to use `@unknown default` when switching.

---

<div class="post-metadata">

**Author:** ![anandabits](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/anandabits/32/355_2.png) [@anandabits](https://forums.swift.org/u/anandabits)\
**Post date:** [April 5, 2019, 2:54pm UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/36 "2019-04-05T14:54:21Z")

</div>

> [@DevAndArtist](#):
>
> This is valid so why can't you still have `CaseIterable` directly sitting near `E` ?

`CaseIterable` would give users a way to get a _guaranteed_ value of all cases. Consider this:

```swift
enum E: CaseIterable {
  private(init) case a
  case b
}

// some other file:
extension E {
   static var a: E {
       for value in allCases {
           switch value {
           case .a: return value
           default: continue
           }
       }
       fatalError()
   }
}

```

By providing a semantically valid conformance to `CaseIterable` you have allowed the case initializer to be written manually in an extension anywhere that can see the enum. `private(init)` is not getting you anything. I think the compiler should at least warn about this and possibly even refuse to synthesize `CaseIterable`. I don't have a strong opinion about which one.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [April 5, 2019, 3:07pm UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/37 "2019-04-05T15:07:13Z")

</div>

When you say `private(init)` will hide the initializer, what exactly do you mean? Do you want to hide the static-like member of the enum type for that specific enum? If so, this would of course mean that the semantics of `access_level(init)` are slightly different than `access_level(call)` would have, as the latter would not hide the (static) type member but prevent calls from certain access levels, which is fairly similar but not entirely the same thing.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [April 5, 2019, 3:13pm UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/38 "2019-04-05T15:13:25Z")

</div>

Wait, why is your example valid code? How can you pattern match against `a` if you cannot construct a new value as it's already restricted by `private(init)`. `case .a: return value` should error out.

---

<div class="post-metadata">

**Author:** ![anandabits](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/anandabits/32/355_2.png) [@anandabits](https://forums.swift.org/u/anandabits)\
**Post date:** [April 5, 2019, 3:15pm UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/39 "2019-04-05T15:15:09Z")

</div>

> [@DevAndArtist](#):
>
> I get that, but that's an artifact of reducing the access level of the initializer and then exposing the initialization value indirectly. There is nothing bad about it that should require the compiler to preventing you from writing such code. It definitely shouldn't be an error.

Of course it should not be an error to write that code yourself. But I’m not sure the compiler should write it for you.

> [@DevAndArtist](#):
>
> When you say `private(init)` will hide the initializer, what exactly do you mean? Do you want to hide the static-like member of the enum type for that specific enum? If so, this would of course mean that the semantics of `access_level(init)` are slightly different than `access_level(call)` would have, as the latter would not hide the (static) type member but prevent calls from certain access levels, which is fairly similar but not entirely the same thing.

Yes, it would hide the `static` property or factory method that the language provides for enum cases. I don’t understand the exact semantics of your `call` idea but it does sound different from what I am talking about here.

---

<div class="post-metadata">

**Author:** ![anandabits](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/anandabits/32/355_2.png) [@anandabits](https://forums.swift.org/u/anandabits)\
**Post date:** [April 5, 2019, 3:16pm UTC](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663/40 "2019-04-05T15:16:03Z")

</div>

> [@DevAndArtist](#):
>
> Wait, why is your example valid code? How can you pattern match against `a` if you cannot construct a new value as it's already restricted by `private(init)` . `case .a: return value` should error out.

Pattern matching an enum does not construct a value, it matches the pattern of a case.

[Previous page](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663.md?page=1)

[Next page](https://forums.swift.org/t/access-control-for-enum-case-initializers/22663.md?page=3)
