Type Conversion When Using Protocol in Swift

Type conversion when using protocol in Swift

Code Different's answer is right, but it's also important to understand why you can't just reinterpret the data without something doing an O(n) conversion to walk though and wrap each element in a box.

[A] is an array with elements the size of A. So, more or less, A[1] is at memory location A[0] + sizeof(A) (this isn't exactly true, but pretend it is; it'll make things simpler).

[Test] has to be able to hold anything that conforms to the protocol. It might be the size of A, it might be the size of B, which is larger. To achieve that, it creates a box that holds "something that conforms to Test". The box is of a known size, and it may have to heap-allocate the thing it points to (or maybe not; depends), but it ain't going to be the same size as both A and B, so the layout of [Test] in memory has to be different than the layout of [A] or [B].

Now as [Test] could do that O(n) walk for you and force a copy right then. Or it delay the copy until the first time you modified something, but it would be much more complicated and it would bury an O(n) (possibly expensive, possibly memory allocating) event inside an as which feels like it should be O(1) and allocate no memory. So there's reasons not to do that.

Some day they may do it anyway to make things simpler on the caller, but there's a cost in both performance and complexity, and so it doesn't do it today and you need to do the map yourself.

If you want a much deeper dive into this stuff, with more details on how this box works (and less "pretend it's simpler than it is"), see Understanding Swift Performance from WWDC 2016.

Swift protocol for things that convert to and from String

This is how you extend RawRespresentable to be conditionally LosslessStringConvertible depending on its RawValue:

extension RawRepresentable where RawValue: LosslessStringConvertible {
init?(_ rv: RawValue) {
self.init(rawValue: rv)
}

var description: String { return self.rawValue.description }
}

Here it is in action:

struct GenericThing<Id: LosslessStringConvertible> {

}

enum Tubbies: String, LosslessStringConvertible {
case dipsy
case laalaa
case po
}

let genericThing = GenericThing<Tubbies>()
print(Tubbies.po is LosslessStringConvertible) // => true

How to convert/cast from a protocol to a class in swift?

With Swift 1.2 / Xcode 6.3 Beta, this compiles:

var cellToReturn = cellProtocol as! UITableViewCell

As of Swift 1.1, You have to cast it to AnyObject or Any, then UITableViewCell. I think this was a kind of bug.

var cellToReturn = cellProtocol as AnyObject as UITableViewCell

ADDED: It turns out that it's a problem of Optional

In this case, cellProtocol is MyTableViewCellProtocol?. you have to unwrap it first, then cast.

Try:

var cellToReturn = cellProtocol! as AnyObject as UITableViewCell
// ^

Cast Protocol to Class in Swift

You are on the right path with optional binding.

as? is a variation of as! that evaluates to an optional of the type to which you are casting. You can use this in conjunction with optional binding:

arr.forEach{ object in
if let a = object as? A { // (object as? A) is of type "A?", but we are unwrapping it
// a is of type A now
}
}

Can you enforce type conversion in a protocol without defining a property/method?

If I understand your question correctly – no, I don't think you can't define an "implicit" conversion that detects and uses a matching init from a specific type. The only way to convert from one type to another in Swift is to explicitly call an init for the "to" type that takes the "from" type, or a function or method on the "from" type that returns the "to" type. There's no way of implementing a protocol that says "use the init for this type with other type, if one is available".

By the way, your ConvertibleToString protocol is essentially a version of Printable (with asString in place of description). So if what you want is to know if something is convertible to a string, you can just check for conformance to Printable. Though note one gotcha – String is not Printable. You can use toString(thing) to convert anything to a string, and it will use Printable where available (and do nothing to convert strings), though this does have the side-effect of giving you a default for non-printable types that you may not want depending on your need.

Note you can require convertibility from something via a protocol:

protocol ConvertibleFromInt {
init(Int)
}

extension String: ConvertibleFromInt { }

extension UInt64: ConvertibleFromInt { }

func gimmeFromInt<T: ConvertibleFromInt>(i: Int) -> T {
return T(i)
}

let s: String = gimmeFromInt(5)
let ui: UInt64 = gimmeFromInt(5)

Swift P.Protocol vs P.Type

P.Protocol is the metatype for the protocol P, just like T.Type is the metatype for the non-protocol type T. There is no "conversions" going on.

So what is P.Type then?

P.Type is an existential metatype. There is no corresponding thing for non-protocol types. Say you want to store metatypes of concrete types that conform to P, you can store it in P.Type:

let x: P.Type = C.self

Note that P.Type can only store metatypes of concrete types, and P.Protocol can only store metatypes of protocols. To see why this division is significant, let's say P defines the static method foo, you can then call x.foo(). This is guaranteed to work because P.Type must have a concrete type, which will implement foo. On the other hand, you can't call foo on P.Protocol, because there is no implementation of foo in P.

See also: https://swiftrocks.com/whats-type-and-self-swift-metatypes

Swift: convert array of actual type to array of protocol type

This Q&A explains the problem. I will suggest a fix that avoids creation of a new array: make your log function generic, and add a type constraint on its type parameter, requiring it to conform to ToString protocol:

func log<T:ToString>( values: [T]) {
values.forEach { print( $0.toString()) }
}

Now Swift lets you call your function with arrays of any type, as long as array elements conform to ToString protocol.



Related Topics



Leave a reply



Submit