Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you overload type-casting operators in Swift?

Tags:

I'd like to implement automatic type conversions between known types in Swift. The C# way of doing it has been overloading type-casting operators. If I want my X type to be cross-assignable with, say, string, I would write:

public class X 
{
     public static implicit operator string(X value)
     {
          return value.ToString();
     }

     public static implicit operator X(string value)
     {
          return new X(value);
     }
}

After that I could write stuff like:

string s = new X();
X myObj = s;

and they would be automatically converted. Is that possible in any way in Swift?

like image 776
Sedat Kapanoglu Avatar asked Jun 07 '18 20:06

Sedat Kapanoglu


1 Answers

No, the language doesn't provide such functionality for custom types. There is bridging between Objective-C collections and Swift collections but that's baked in and not customizable.

// Swift array of `String` elements
let swiftArray: [String] = ["Bob", "John"] 

// Obj-C array of `NSString` elements, but element type information 
// is not known to the compiler, so it behaves like an opaque NSArray
let nsArray: NSArray = ["Kate", "Betty"] 

// Obj-C array of an `NSString` and an `NSNumber`, element type
// information is not known to the compiler
let heterogeneousNSArray: NSArray = ["World", 3]

// Casting with `as` is enough since we're going from Swift array to NSArray 
let castedNSArray: NSArray = swiftArray as NSArray

// Force casting with `as!` is required as element type information 
// of Obj-C array can not be known at compile time
let castedSwiftArray: [String] = nsArray as! [String]

// Obj-C arrays can not contain primitive data types and can only 
// contain objects, so we can cast with `as` without requiring a 
// force-cast with `!` if we want to cast to [AnyObject]
let heterogeneousCastedNSArray: [AnyObject] = heterogeneousNSArray as [AnyObject]

Documentation for type casting is available here.

I think you can achieve what you want to do with initializers.

extension X {
    init(string: String) {
        self = X(string)
    }        
}

extension String {
    init(x: X) {
        // toString is implemented elsewhere
        self = x.toString
    }        
}

let x = X()
let string = "Bobby"

let xFromString: X = X(string: string)
let stringFromX: String = String(x: x)

Not directly related to your question but there is also a family of protocols that start with ExpressibleBy..., enabling you to do things like the following: Let's say we want to initialize strings from integer literals. We can do that by conforming to and implementing ExpressibleByIntegerLiteral

// Strings can not be initialized directly from integer literals
let s1: String = 3 // Error: Can not convert value of type 'Int' to specified type 'String'

// Conform to `ExpressibleByIntegerLiteral` and implement it
extension String: ExpressibleByIntegerLiteral {
  public init(integerLiteral value: Int) {
    // String has an initializer that takes an Int, we can use that to 
    // create a string
    self = String(value)
  }
}

// No error, s2 is the string "4"
let s2: String = 4 

A nice use case for ExpressibleByStringLiteral can be found here.

like image 115
akaralar Avatar answered Oct 14 '22 04:10

akaralar