Reputation: 30458
I'm seeing something that doesn't make sense to me. Here's my code...
import Foundation
enum SeparatorPaddingLocation{
case beforeSeparator
case afterSeparator
}
struct SeparatorToken{
let separator : Character
let formattedSeparator : String
let paddingLocation : SeparatorPaddingLocation
}
extension Array where Element == String {
func aligned(separatorTokens:[SeparatorToken]) -> [String] {
// This won't compile!
let remainingSeparatorTokens = Array(separatorTokens.dropFirst())
return self
}
}
The 'let' causes this compile error even though dropFirst()
returns an ArraySlice<SeparatorToken>
.
Type of expression is ambiguous without more context
Why is this ambiguous? What am I missing?
I also tried fully qualifying it like so, but this didn't work either...
let remainingSeparatorTokens:[SeparatorToken] = Array(separatorTokens.dropFirst())
Upvotes: 1
Views: 308
Reputation: 3203
This is not a bug, and there's nothing mysterious or bug-like going on.
Not a bug: it's the very reason the ==
operator is available in where
clauses: to require Element
to be of type String
, as explained here. Search the page for "you can also write a generic where clauses that require Item to be a specific type".
Not bug-like: when you're inside the extension and call an Array
initializer, the compiler takes your where
clause to indicate what specialization of Array
you want. Your where
clause says String
, so Array<String>
it is. The compiler can't read your mind when you pass an ArraySlice<SeparatorToken>
into an Array<String>
initializer. Did you make a mistake, or did you really want an Array<ArraySlice<SeparatorToken>>
? Hence the error message about ambiguity.
Here's a simplified and annotated version of OP's code, in case anyone finds it illuminating.
class Foo<T> {
var theFoo: T!
init() { theFoo = nil }
init(_ aFoo: T) { theFoo = aFoo }
}
extension Foo where T == Int {
func bar() {
// This one fails, because our where clause
// says this extension applies to Foo<Int>,
// but now we're trying to assign it to a Foo<String>
let aFoo: Foo<String> = Foo()
// This one also fails, for the same reason.
// It uses the other initializer, but we're
// still inside the Foo<Int> extension.
let bFoo: Foo<String> = Foo("bFoo")
// This one works, because we're expressly
// overriding the where clause
let cFoo: Foo<String> = Foo<String>("cFoo")
}
}
The ambiguity error makes sense. I told the compiler in my where
clause to make this an Int
extension, so it created Foo<Int>
s. But then I tried to assign those to variables of type Foo<String>
. The compiler couldn't tell whether I really wanted Foo<String>
or I had made a mistake, so it gave up.
Upvotes: 1
Reputation: 299605
Inside of generic extensions, type parameters bind to their scope. I'm not sure this is really a good thing (or possibly if it's really a bug), but it shows up consistently, so the type parameter here implicitly wants to be String
.
A simpler example:
let x = Array() // error: generic parameter 'Element' could not be inferred
That makes sense. But put it in an extension:
extension Array {
func f() {
let x = Array()
}
}
This compiles, and x
is of type [Element]
.
In your case, you've set Element
to String
, so the call to Array.init
wants to create a [String]
and it's getting confused because SeparatorToken != String
. You need to pass the type parameter explicitly (as you've discovered):
let remainingSeparatorTokens = Array<SeparatorToken>(separatorTokens.dropFirst())
Again, I don't really consider this a feature in Swift, and it may even be considered a bug. But it's very consistent.
Upvotes: 6
Reputation: 5823
I just run your code in playground and updated as follows, then error is gone:
import UIKit
import Foundation
enum SeparatorPaddingLocation{
case beforeSeparator
case afterSeparator
}
struct SeparatorToken{
let separator : Character
let formattedSeparator : String
let paddingLocation : SeparatorPaddingLocation
}
extension Array where Element == String {
func aligned(separatorTokens:[SeparatorToken]) -> [String] {
// I update code here
let remainingSeparatorTokens = Array<SeparatorToken>(separatorTokens.dropFirst())
return self
}
}
As per swift, Array required type when you going to initialise with Array.
I hope this will work for you.
For more details about array visit: https://developer.apple.com/documentation/swift/array
Upvotes: 1