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'
source to share
-
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 theString
default one).Put it this way: yours
as
performs a similar function withas
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 ambiguousf
Swift calls . It's the same one that gets out withinit(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 classPerson
to a function that took anEmployee
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 writeinit
forEmployee
that takesPerson
(or some class or protocol that matchesPerson
) and then calls it.
source to share