Swift: collecting collections and creating custom convertible protocols

Consider this class Person

, which simply implements StringLiteralConvertible

and assigns a string literal name

:

class Person : StringLiteralConvertible {
    var name : String?

    typealias StringLiteralType = String

    required init(stringLiteral value: StringLiteralType) {
        println("stringLiteral \(value)")
        name = value
    }

    typealias ExtendedGraphemeClusterLiteralType = String

    required init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) {
        println("extendedGraphemeClusterLiteral \(value)")
        name = value
    }

    typealias UnicodeScalarLiteralType = Character

    required init(unicodeScalarLiteral value: UnicodeScalarLiteralType) {
        println("unicodeScalarLiteral \(value)")
        name = "\(value)"
    }
}

      

This allows me to create an instance Person

using the line:

let aaron : Person = "Aaron"

      

I can even extract an array from Person

from an array of strings:

let names = ["John", "Jane"] as [Person]

      

However, this only works with string literals. If I use a string variable it fails:

let aaronString = "Aaron"
let aaron : Person = aaronString
// Error: 'NSString' is not a subtype of 'Person'

      

Likewise, an attempt to dump an array of nonlibrary strings fails:

let nameStrings = ["John", "Jane"]
let people : [Person] = nameStrings
// Error: 'String' is not identical to 'Person'

      

I have three questions:

  • Is there some other protocol I can implement to pass a non-literal string to Person

    ? I would like to do this so that I can create entire collections to transform objects.

  • If not to # 1, is map + an initializer the best way to do the conversion yourself?

    let nameStrings = ["John", "Jane"]
    let people = nameStrings.map{Person(name: $0)}
    
          

  • If so to # 1, is there a similar approach that I can use to specify an approach to transform two objects that are not related in a hierarchy? That is, can I get around this error without an initializer?

    let rikerPerson : Person = "Riker"
    let rikerEmployee = rikerPerson as Employee
    // Error: 'Person' is not convertible to 'Employee'
    
          

+3


source to share


1 answer


  • What you describe as "casting" is really not casting (in the sense that, say s = "fred"; ns = s as NSString

    , or what is in C ++).

    let names = ["John", "Jane"] as [Person]
    
          

    is another way to write:

    let names: [Person] = ["John", "Jane"]
    
          

    that is, a way to tell Swift which possible version to StringLiteralConvertible

    use (not the String

    default one).

    Put it this way: yours as

    performs a similar function with as

    in this snippet, which disambiguates two overloaded functions that differ only in return type:

    func f() -> String { return "foo" }
    func f() -> Int { return 42 }
    
    let i = f() as Int    // i will be 42
    let s = f() as String // s will be "foo"
    
          

    There is no "conversion" here - as

    only used to eliminate ambiguous f

    Swift calls . It's the same one that gets out with init(stringLiteral:)

    .

  • Definitely (but only if you put a space between map

    and { }

    ;-).

    If you're worried about turning it all into an array in order to do something different with it, check lazy(a).map

  • Not. Beta releases used a method __conversion() -> T

    that you could implement to do casts like this on your own classes, or more importantly, allow you to pass your class Person

    to a function that took an Employee

    argument and it will be implicitly converted. But it disappeared. Typically, this implicit conversion is the opposite of the Swifts style, except in rare cases (Obj-C and C interop and implicit boxing in options that are basic). You have to write init

    for Employee

    that takes Person

    (or some class or protocol that matches Person

    ) and then calls it.



+3


source







All Articles