1

Given a struct Parameter which has a name and a value property, I would like to add a Subscript to an Array which let me return the value of the first found element whose name matches the subscript parameter.

Usage example:

let params = [Parameter("firstName", "John"), Parameter("lastName", "Doh")]    
let value = params["firstName"] // returns "John"

Edit: The compiler error occurs only, if I define the array as follows:

let params: [ParameterType] = [Parameter("firstName", "John"), Parameter("lastName", "Doh")]    
let value = params["firstName"] // returns "John"

Now, when I implement it, I get a compiler error:

error: ambiguous reference to member 'subscript'
        let value = params["firstName"] // returns "John"
                    ^~~~~~

note: found this candidate
    public subscript (index: Int) -> Element { get set }

note: found this candidate
    public subscript (name: String) -> String? { get set }

note: found this candidate
    public subscript (bounds: Range<Self.Index>) -> MutableSlice<Self> { get set }

note: found this candidate
    public subscript (position: Self.Index) -> Self.Base.Generator.Element { get }

So, why is this ambiguous?

That doesn't make sense to me.

Note: all the other candidates are implementations for struct Array defined within the standard library.

For example how is

subscript (index: Int) -> Element and

subscript (name: String) -> String?

ambiguous?

I suspect, the error messages are just confusing, and the true error is because type parameter Element is not fully specified. Anyway, how could this be implemented?


This is my attempt to implement it.

I am aware, that - due to the current limitations in the language - the subscript implementation is actually defined for a set of Element types conforming to ParameterType:

public protocol ParameterType {
    init(_ name: String, _ value: String)
    var name: String { get }
    var value: String { get }
}

public extension Array where Element: ParameterType {

    subscript(name: String) -> String? {
        get {
            let a = self.filter { $0.name == name }
            if let e = a.first {
                return e.value
            } else {
                return nil
            }
        }
        set(newValue) {
            let i = self.index { $0.name == name }
            if let index = i {
                if let value = newValue {
                    self[index] = Element(name, value)
                } else {
                    self.remove(at:index)
                }
            } else {
                if let value = newValue {
                    let param = Element(name, value)
                    self.append(param)
                } else { /* */ }
            }
        }
    }

}

And here is one concrete type conforming to ParameterType:

public struct Parameter: ParameterType {
    public init(_ name: String, _ value: String) {
        self.name = name
        self.value = value
    }
    public var name: String
    public var value: String
}

Note:

Constraining the array to a class type doesn't solve it: the same errors will be emitted.

johndpope
  • 5,035
  • 2
  • 41
  • 43
CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67
  • Cannot reproduce. Your code compiles for me, and `params["firstName"]` returns `Optional("John")`. – Martin R Mar 08 '16 at 09:26
  • @MartinR This is Version 7.3 beta (7D152p) (beta 4). I'll Check this with beta 5 and the most recent release. – CouchDeveloper Mar 08 '16 at 09:28
  • @MartinR I think I found the issue: I defined my array as this: ` var params: Parameters = [param]` where `Parameters` equals `typealias Parameters = [ParameterType]` . If I omit the explicit type annotation - as in my sample above - it works as expected. Still, now I'm wondering why ... ;) – CouchDeveloper Mar 08 '16 at 09:40
  • This looks like a duplicate of http://stackoverflow.com/questions/33112559/protocol-doesnt-conform-to-itself: With `let params : [ParameterType] = ...` the array elements are of the *protocol type* ParameterType, and a protocol does not conform to itself. Without the annotation, `params` has the type `[Parameter]` and the elements are of the *concrete type* Parameter. – Martin R Mar 08 '16 at 09:47
  • @MartinR Yes, it seems to be this same issue. Interestingly: the current release will emit a more clear error message. The beta version's error messages are just confusing - if not incorrect. – CouchDeveloper Mar 08 '16 at 09:50

0 Answers0