2

I'm trying to implement a padding function for Array:

extension Array {
    func dataWithPadding(offset: Int, length: Int, paddingLength: Int) -> NSData {
        var arr = Array(self[offset..<(offset + length)])
        arr = arr.reverse()
        for (var i = 0; i < paddingLength; i++) {
            arr.append(0)
        }
        let d = NSData(bytesNoCopy: &arr, length: length)
        return d
    }
}

This errors at arr.append with:

Cannot invoke 'append' with an argument list of type '(Int)'

I try to change the declaration into:

var arr[UInt8] = Array(self[offset..<(offset + length)])

However, this also errors:

Cannot assign to immutable expression of type '[UInt8].Type' (aka 'Array.Type')

The weird part: I try to run the original code with arr.append commented out, and use lldb to directly run arr.append(0), it actually works.

I'm using Xcode 7.2.

1 Answer 1

3

Since your Array extension puts no constraint on which types of arrays (element types) that can be used with it, .append(0) cannot be invoked; not all types can be converted into from integer literals. Hence, it's not weird that you cannot use .append(0) in the "generic" array extension, whereas you naturally can use it directly on an array that Swift can infer to have integer literal convertible elements, e.g. [Int]. Consider the following example:

var arr : [UInt8] = []
arr.append(0)

var arr2 : [String] = []
arr2.append(0)      // error

In the example above, both arrays would have access to your extension dataWithPadding, but both can naturally not make use of arr.append(0) in the extension, hence the error message

Cannot invoke 'append' with an argument list of type '(Int)'

Now, a simple fix is to add a type constraint for the array elements to IntegerLiteralConvertible, after which you extension is valid, and accessible for all arrays that have elements which conform to IntegerLiteralConvertible.

extension Array where Element: IntegerLiteralConvertible {
    func dataWithPadding(offset: Int, length: Int, paddingLength: Int) -> NSData {
        var arr = Array(self[offset..<(offset + length)])
        arr = arr.reverse()
        for (var i = 0; i < paddingLength; i++) {
            arr.append(0)
        }
        let d = NSData(bytesNoCopy: &arr, length: length)
        return d
    }
}

Alternatively, make use of the less general SignedNumberType, UnSignedIntegerType or IntegerType as type constraint for Element; these conform also to e.g. Comparable and Equatable, in case you'd like to compare and perform operations on your generic elements in the extension.


Finally note that you can naturally use your own custom protocol as type constraint for Element in your extension, allowing you to include additional blueprints (below, foo() method) accessible for your Element:s in the extension

protocol MyIntegerLiteralInitializableTypes: IntegerLiteralConvertible {
    func foo()
}
extension MyIntegerLiteralInitializableTypes {
    func foo() {
        print("I am of type \(self.dynamicType)!")
    }
}

/* For the Array<Type>:s you want to have access to .dataWithPadding,
extend those 'Type':s to MyIntegerLiteralInitializableTypes           */
extension Int8 : MyIntegerLiteralInitializableTypes { }
extension UInt8 : MyIntegerLiteralInitializableTypes { }

extension Array where Element: MyIntegerLiteralInitializableTypes {
    func dataWithPadding(offset: Int, length: Int, paddingLength: Int) -> NSData {

        self.first?.foo() /* I am of type ...! */

        var arr = Array(self[offset..<(offset + length)])
        arr = arr.reverse()
        for (var i = 0; i < paddingLength; i++) {
            arr.append(0)
        }
        let d = NSData(bytesNoCopy: &arr, length: length)
        return d
    }
}

It's also probably a good idea to add @warn_unused_result to your dataWithPadding(...) function signature, as a call to it without assigning the return will yield a runtime exception ("... malloc: ...: pointer being freed was not allocated").

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks. Can I conform to other protocol while using where clause?
@skyline75489 Absolutely. An alternative is IntegerType, or a custom protocol of yours. For the latter, you must make sure the protocol includes the necessary blueprints for converting an integer literal into the element value.
Cool. Thanks man. Gotta say, the error is quite misleading for someone like me who does not know generics in Swift that well.
@skyline75489 Happy to help. To fully close the discussion for above, I'll add a simple example of writing your own custom protocol to use as type constraint.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.