Public struct init is unexpectedly internal

So I’ve got this code in a package called “SomeLib":
public struct SomeType {
    public var text = "SomeText"
}
and then, in another package, write this:
import SomeLib
print(SomeType().text)
and then run swift build, I get this error:
error: 'SomeType' initializer is inaccessible due to 'internal' protection level

"Well that’s odd… there isn’t even an initializer to be internal or public”, I said to myself. Then I proceeded to futz around with it for a while before having a lightbulb moment:
public struct SomeType {
    public var text = "SomeText"
    public init() {} //this fixes it
}

In cases like this where the struct is public and all its stored properties are both public and already have values, should we make the implicit init() function also be public? Seems like the “least surprising” thing to do.

- Dave Sweeris

1 Like

So I’ve got this code in a package called “SomeLib":
public struct SomeType {
    public var text = "SomeText"
}
and then, in another package, write this:
import SomeLib
print(SomeType().text)
and then run swift build, I get this error:
error: 'SomeType' initializer is inaccessible due to 'internal' protection level

"Well that’s odd… there isn’t even an initializer to be internal or public”, I said to myself. Then I proceeded to futz around with it for a while before having a lightbulb moment:
public struct SomeType {
    public var text = "SomeText"
    public init() {} //this fixes it
}

In cases like this where the struct is public and all its stored properties are both public and already have values, should we make the implicit init() function also be public? Seems like the “least surprising” thing to do.

This is intentional. I believe the core team’s rationale is that public APIs should always be explicitly written down in source. So your example above defining a public init() is correct.

Slava

···

On Jan 30, 2017, at 1:12 AM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

- Dave Sweeris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Unfortunately, I've come across this several times, but read here that it's intended behavior. If I remember correctly, it has something to do with the automatically generated initializers - it's implicitly internal so that you don't expose the initializer by mistake.

The most annoying about it is, however, when the constructor has several parameters - when you declare

  public var text: String

Swift automatically generates an initializor that takes a text parameter, but it is internal. When you have 4-5 members, the initializer gets quite long.

And if you want to have it public, you need to declare it and then assign each member, which is tedious. I'd prefer some kind of an annotation on the struct, for example:

@init(public)
public struct SomeType { ... }

···

On Jan 30, 2017, at 10:12 AM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

So I’ve got this code in a package called “SomeLib":
public struct SomeType {
    public var text = "SomeText"
}
and then, in another package, write this:
import SomeLib
print(SomeType().text)
and then run swift build, I get this error:
error: 'SomeType' initializer is inaccessible due to 'internal' protection level

"Well that’s odd… there isn’t even an initializer to be internal or public”, I said to myself. Then I proceeded to futz around with it for a while before having a lightbulb moment:
public struct SomeType {
    public var text = "SomeText"
    public init() {} //this fixes it
}

In cases like this where the struct is public and all its stored properties are both public and already have values, should we make the implicit init() function also be public? Seems like the “least surprising” thing to do.

- Dave Sweeris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Oh, I knew (well, suspected) it was intentional… I just didn't recall if we’d discussed this specific scenario before. If we did, then I don’t want to rehash it.

- Dave Sweeris

···

On Jan 30, 2017, at 1:21 AM, Slava Pestov <spestov@apple.com> wrote:

On Jan 30, 2017, at 1:12 AM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

So I’ve got this code in a package called “SomeLib":
public struct SomeType {
    public var text = "SomeText"
}
and then, in another package, write this:
import SomeLib
print(SomeType().text)
and then run swift build, I get this error:
error: 'SomeType' initializer is inaccessible due to 'internal' protection level

"Well that’s odd… there isn’t even an initializer to be internal or public”, I said to myself. Then I proceeded to futz around with it for a while before having a lightbulb moment:
public struct SomeType {
    public var text = "SomeText"
    public init() {} //this fixes it
}

In cases like this where the struct is public and all its stored properties are both public and already have values, should we make the implicit init() function also be public? Seems like the “least surprising” thing to do.

This is intentional. I believe the core team’s rationale is that public APIs should always be explicitly written down in source. So your example above defining a public init() is correct.

See: https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md

-Chris

···

On Jan 30, 2017, at 1:26 AM, Charlie Monroe via swift-evolution <swift-evolution@swift.org> wrote:

Unfortunately, I've come across this several times, but read here that it's intended behavior. If I remember correctly, it has something to do with the automatically generated initializers - it's implicitly internal so that you don't expose the initializer by mistake.

The most annoying about it is, however, when the constructor has several parameters - when you declare

  public var text: String

Swift automatically generates an initializor that takes a text parameter, but it is internal. When you have 4-5 members, the initializer gets quite long.

And if you want to have it public, you need to declare it and then assign each member, which is tedious. I'd prefer some kind of an annotation on the struct, for example:

@init(public)
public struct SomeType { ... }

On Jan 30, 2017, at 10:12 AM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

So I’ve got this code in a package called “SomeLib":
public struct SomeType {
    public var text = "SomeText"
}
and then, in another package, write this:
import SomeLib
print(SomeType().text)
and then run swift build, I get this error:
error: 'SomeType' initializer is inaccessible due to 'internal' protection level

"Well that’s odd… there isn’t even an initializer to be internal or public”, I said to myself. Then I proceeded to futz around with it for a while before having a lightbulb moment:
public struct SomeType {
    public var text = "SomeText"
    public init() {} //this fixes it
}

In cases like this where the struct is public and all its stored properties are both public and already have values, should we make the implicit init() function also be public? Seems like the “least surprising” thing to do.

- Dave Sweeris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

So I’ve got this code in a package called “SomeLib":
public struct SomeType {
    public var text = "SomeText"
}
and then, in another package, write this:
import SomeLib
print(SomeType().text)
and then run swift build, I get this error:
error: 'SomeType' initializer is inaccessible due to 'internal' protection level

"Well that’s odd… there isn’t even an initializer to be internal or public”, I said to myself. Then I proceeded to futz around with it for a while before having a lightbulb moment:
public struct SomeType {
    public var text = "SomeText"
    public init() {} //this fixes it
}

In cases like this where the struct is public and all its stored properties are both public and already have values, should we make the implicit init() function also be public? Seems like the “least surprising” thing to do.

This is intentional. I believe the core team’s rationale is that public APIs should always be explicitly written down in source. So your example above defining a public init() is correct.

Oh, I knew (well, suspected) it was intentional… I just didn't recall if we’d discussed this specific scenario before. If we did, then I don’t want to rehash it.

I think there was a proposal floating around for ‘flexible member-wise initialization’. This would allow explicitly defining an initializer (to make it public or @_inlineable or whatever) without writing out the boilerplate to initialize all members.

I don’t have the link offhand, and I don’t remember what the conclusion was, but if you’re interested you might want to look into it and revive it. ;-)

Slava

···

On Jan 30, 2017, at 1:30 AM, David Sweeris <davesweeris@mac.com> wrote:

On Jan 30, 2017, at 1:21 AM, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Jan 30, 2017, at 1:12 AM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

- Dave Sweeris

So I’ve got this code in a package called “SomeLib":
public struct SomeType {
    public var text = "SomeText"
}
and then, in another package, write this:
import SomeLib
print(SomeType().text)
and then run swift build, I get this error:
error: 'SomeType' initializer is inaccessible due to 'internal' protection level

"Well that’s odd… there isn’t even an initializer to be internal or public”, I said to myself. Then I proceeded to futz around with it for a while before having a lightbulb moment:
public struct SomeType {
    public var text = "SomeText"
    public init() {} //this fixes it
}

In cases like this where the struct is public and all its stored properties are both public and already have values, should we make the implicit init() function also be public? Seems like the “least surprising” thing to do.

This is intentional. I believe the core team’s rationale is that public APIs should always be explicitly written down in source. So your example above defining a public init() is correct.

Oh, I knew (well, suspected) it was intentional… I just didn't recall if we’d discussed this specific scenario before. If we did, then I don’t want to rehash it.

I think there was a proposal floating around for ‘flexible member-wise initialization’. This would allow explicitly defining an initializer (to make it public or @_inlineable or whatever) without writing out the boilerplate to initialize all members.

I don’t have the link offhand, and I don’t remember what the conclusion was, but if you’re interested you might want to look into it and revive it. ;-)

I was the author of this proposal. It was deferred. The review brought a lot of good ideas to light so it will look somewhat different when it gets revived. I have been assuming it is out of scope for Phase 1 but plan to revive it when the time comes.

If this *is* in scope right now please let me know and I’ll find some time to start a new discussion thread outlining two possible directions we could take.

···

On Jan 30, 2017, at 3:32 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 30, 2017, at 1:30 AM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:

On Jan 30, 2017, at 1:21 AM, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Jan 30, 2017, at 1:12 AM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Slava

- Dave Sweeris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

3 Likes