Can't Subscript from swift extension - arrays

This is the problem:
typealias Byte = UInt8
protocol ByteProtocol {}
extension UInt8: ByteProtocol {}
extension Array where Element: ByteProtocol {
subscript (index: Int) -> UInt8 {
return self[Int(index % self.count)]
}
}
This gives me Overflow even if it is mathematically impossible:
var p: [Byte] = [Byte]()
p.append(15)
print(p[10])
So what is the mistake here? P.S. Thank you for your answer :)

You can't overload subscripts this way. Even if you could, you'd be creating an infinite loop in your implementation. Your implementation also would be illegal, since it returns something other than Element.
What you mean is something like this:
extension Array where Element: ByteProtocol {
subscript (wrapping index: Int) -> Element {
return self[Int(index % self.count)]
}
}
var p: [Byte] = [Byte]()
p.append(15)
print(p[wrapping: 10])

It doesn't give you an "overflow". It gives you an out-of-range error. There is no element index 10 in an array with only 1 element. The crash occurs before your subscript implementation is ever called (as you could easily discover by breakpointing it). You cannot magically change the meaning of an existing subscript implementation in the way you are hoping to.

The default implementation of subscript is called, not yours. Hence, it's trying to actually access the 10th element, which doesn't exist.
You can't override the behaviour of a struct like Array using an extension. They're not polymorphic. You can, however, add a new definition of a subscript, as rob showed.

Related

How is Array() ambiguous when the given argument is a known type?

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())
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.
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
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.

AnyType error in Swift array

Trying to make and shuffle a card deck in Swift, made up of Card objects in an array called cardDeck. Then I want to shuffle it. Relevant code:
var cardDeck = [card]()
for ind in 1 ... 4
{
for ind2 in 1 ... 13
{
cardDeck.append(card(number: ind2, color: ind))
}
}
cardDeck = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(cardDeck)
"Cannot assign value of type [anyobject] to logic.card" ("logic" is the overall class name, and Card is another class within it)
Anyone know whats wrong? I guess the array is not an AnyObject type of array since I declared it as containing cards, right?
The problem is that arrayByShufflingObjectsInArray takes an [AnyObject] and returns an [AnyObject].
Therefore its going to completely throw away your [Card] type information that you provide as an input, and thus give you an error about not being able to convert types when you try to assign the output back to your original array. I suspect this is due to the method being implemented in Objective-C, not Swift.
A more Swifty version of the method would look like this:
func arrayByShufflingObjectsInArray<T:AnyObject>(array:[T]) -> [T] {
...
}
Through using generics, you can preserve the type information that you pass in to begin with, returning the same type that you inputted. In fact, you can write your own extension of GKRandomSource in order to do just that:
extension GKRandomSource {
func arrayOfSameTypeByShufflingObjectsInArray<T:AnyObject>(array:[T]) -> [T] {
return arrayByShufflingObjectsInArray(array) as! [T]
}
}
(Feel free to come up with a more catchy name for the method)
The force downcast is used as an ugly solution to the problem – which is why I recommend creating an extension, rather than using this directly. It cannot crash, as the output array is guaranteed to contain objects of the same type as the input (as the array you pass in can only contain a single type).
You can now use it like so:
cardDeck = GKRandomSource.sharedRandom().arrayOfSameTypeByShufflingObjectsInArray(cardDeck)

Error Returning Array<Struct> as Array<Any>

I have a function that can return several different Types depending on the internal conditions of my data structure, so I return an Array of Any and leave a comment explaining the types it could be. (I'm sure there's a better solution, but I don't know what it is). That gave me the error
Cannot convert return expression of type '[S]' to return type '[Any]'
where S is a pure-Swift struct. I've boiled this down to a toy example that exemplifies the issue:
protocol P { } // protocol
struct S: P { } // struct conforming to protocol
// Will Compile: all protocols implicitly conform to Any
func returnAny() -> Any {
return S()
}
// refuses to compile with above error
func returnArrayOfAny() -> [Any] {
return [S]()
}
// oddly enough, this will compile and work
func returnMappedArrayOfAny() -> [Any] {
return [S]().map { $0 }
}
Am I missing something in the way Arrays or Protocols work in Swift? Casting [S]() as! [P] also allows the function to return, although I'm not sure why the cast has to be forced, since S does conform to P.
I've made a Playground with the issue
This is the same issue discussed by Brent Simmons in this blog post. There is some interesting discussion on Twitter about it; I would encourage you to read through the conversation.
The main point is that [S] and [Any] may have fundamentally different storage/layout, so a cast between them is nontrivial (can't necessarily be done in constant time). map is a convenient way around this, making it explicit that you're building up a new array.
In the case of an empty array, you can simply return [].

Is it possible to create an array of functions that take "inout" parameters in Swift?

I am running into something that I think might be a bug. Swift lets you create an array of functions, like this:
func example1(a: Int) {
println(a)
}
let functionArray1 = [example1]
println(functionArray1[0](3)) // This prints "3"
But if I try to create an array of functions that take an inout parameter, I get the dreaded segmentation fault 11:
func example2(inout a: Int) {
println(a)
}
let functionArray2 = [example2] // This produces the seg fault
It makes no difference if I actually manipulate a inside the function or not.
Does anyone know what is going on here? Is there a way to create an array of functions with an inout parameter by using an appropriate type annotation, or some other trick?
EDIT: I have tried providing a type annotation for the array - that does not solve the problem:
let functionArray2: [(inout Int) -> ()] = [example2] // Same error...
I have also tried writing the function as a closure (with an explicit type annotation) and then adding the closure to an array, also with no success.
As the comments on the question have pointed out, this does appear to be a bug relating to Swift's handling of inout in type signatures for some cases.
Until the bug is addressed, you can work around it by using an UnsafeMutablePointer.
func increment(a: UnsafeMutablePointer<Int>) {
a.memory += 1
}
let functionArray = [increment]
var thing = 2
functionArray2[0](&thing)
println(thing) // 3

Swift optional Array property is immutable?

I am constructing an array of booleans to store the state of the sections in a UICollectionView. It is a variable stored as a property of my UIViewController:
var _weekSelections : Array<Bool>!
Then, in a function called by loadView(), I construct the array and assign a value to the first index:
_weekSelections = Array<Bool>(count:_weekCount, repeatedValue:false)
_weekSelections[0] = true
The value at index 0 remains false! The array is constructed, and has multiple elements, but any assignment that I make to an index does not affect the value stored at that index, even if I check the value on the very next line of code. I know that Swift makes a copy of an array if I perform an action that may change its length, but I don't think this is a case where a copy would me made. The only way I can get any value to change is if I manually create a copy as follows:
var copy = _weekSelections
copy[0] = true
_weekSelections = copy
Am I missing something obvious or could this be a strange bug?
For the sake of having my code on SO rather than Pastebin, here's my observation. This looks like some kind of bug or unexpected behaviour when using an optional array in a Swift class derived from an Objective C class. If you use a plain Swift class, this works as expected:
class Foo {
var weekSelections: Array<Bool>!
func test() {
weekSelections = Array<Bool>(count: 10, repeatedValue: false)
weekSelections[0] = true;
println(weekSelections[0]) // Prints "true"
}
}
var foo = Foo()
foo.test()
However, if you derive Foo from NSObject:
import Foundation
class Foo : NSObject { // This derivation is the only difference from the code above
var weekSelections: Array<Bool>!
func test() {
weekSelections = Array<Bool>(count: 10, repeatedValue: false)
weekSelections[0] = true;
println(weekSelections[0]) // Prints "false"
}
}
var foo = Foo()
foo.test()
Even in this case, if you do your weekSelections initialisation in an initialiser, then it works:
class Foo : NSObject {
var weekSelections: Array<Bool>!
init() {
weekSelections = Array<Bool>(count: 10, repeatedValue: false)
weekSelections[0] = true;
println(weekSelections[0]) // Prints "true"
}
}
var foo = Foo()
Personally, I'd say that this is a bug. I can't see anything in any documentation that would explain the difference in behaviour when derived from NSObject.
I also can't see anything that says that optional array properties would be immutable. This would be especially strange when you consider that "immutable" arrays are actually mutable in Swift, i.e. this:
// Use "let" to declare an "immutable" array
let weekSelections = Array<Bool>(count: 10, repeatedValue: false)
weekSelections[0] = true;
println(weekSelections[0]); // Prints "true"; arrays are never really "immutable" in Swift
...works fine, and is currently documented as being valid, even if it seems a bit odd.
Personally, I'd use whatever workaround you can and raise a bug with Apple, to see what light they can shed.
Not an explanation, but a workaround. The issue isn't with the count:repeatedValue initializer, but rather with the array being assigned to an optional variable. From what I can tell optional arrays can only use accessor methods and not mutator methods--effectively they're immutable. Temporarily assigning _weekSelections to a non-optional variable before attempting to change its contents (and assigning back to _weekSelections when done) will work. Please note that this seems to create a new array (with the same elements) on assignments, so there may be memory issues to consider if the array is very big. Of course, simply using a non-optional variable in the first place will also work.
As for why optional arrays aren't mutable, that may be a bug or there may be some esoteric reason for it I'm not fathoming. Anyone else have a plausible-sounding theory?

Resources