1

I wanted to store ListStyle in a property inside SupportOpts, so I added the <S> and where S: ListStyle to the struct declaration (from this answer).

struct SupportOpts<S> where S: ListStyle {
    var title: String = "Placeholder"
    var listStyle: S = InsetGroupedListStyle() as! S /// had to put an `as! S` or else it couldn't compile
}

I can make an instance of it like this:

let options: SupportOpts = SupportOpts(
    title: "Title",
    listStyle: InsetGroupedListStyle()
)

But, when I remove InsetGroupedListStyle() (I already set a default value inside SupportOpts):

let options: SupportOpts = SupportOpts(
    title: "Title"
)

I get:

Generic parameter 'S' could not be inferred, Explicitly specify the generic arguments to fix this issue [Fix]

So I pressed the Fix button, and it's now expecting a S: ListStyle: error "Cannot find type '<#S: ListStyle#>' in scope"

What should I put here?

let options: SupportOpts = SupportOpts<ListStyle>(title: "Title")
/// Error: Value of protocol type 'ListStyle' cannot conform to 'ListStyle'; only struct/enum/class types can conform to protocols

let options: SupportOpts = SupportOpts<S>(title: "Title")
/// Error: Cannot find type 'S' in scope (well that's kind of obvious...)

let options: SupportOpts = SupportOpts<InsetGroupedListStyle>(title: "Title")
/// this works...

Do I have to specify SupportOpts<InsetGroupedListStyle>(title: "Title"), even if I already set a default inside SupportOpts?

aheze
  • 24,434
  • 8
  • 68
  • 125

1 Answers1

1

You can tell that something has gone wrong when you needed to add as!. That isn't the right approach. Consider the case if someone chose a different S:

let options = SupportOpts<InsetListStyle>(title: "Title")

This would have to crash.

Instead, you want an extension to make things more convenient.

struct SupportOpts<S> where S: ListStyle {
    var title: String = "Placeholder"
    var listStyle: S     // No default value here
}

Your goal is to have an init that just takes title, so you add that:

extension SupportOpts where S == InsetGroupedListStyle {
    init(title: String) {
        self.init(title: title, listStyle: InsetGroupedListStyle())
    }
}

And type inference will work out the rest:

let options = SupportOpts(title: "Title")
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • But what if I have another struct that contains `var options: SupportOpts` (just the type, no initialization)? It doesn't work... I get "Reference to generic type 'SupportOpts' requires arguments in <...>" – aheze Oct 26 '20 at 01:50
  • Ok, I fixed it with `struct OtherStruct: View where S: ListStyle { var options: SupportOpts }`, not sure if that's the right way though – aheze Oct 26 '20 at 01:55
  • Yes, that's correct. You can also write it `struct OtherStruct: View`. The two are identical. – Rob Napier Oct 26 '20 at 14:19