How to implement multiple extensions for the same protocol?

In Rust, we can write the following code:

trait Add<Rhs> {
    type Output;
    fn add(self, rhs: Rhs) -> Self::Output;
}

struct Point {
    x: i32,
    y: i32,
}

impl Add<Point> for Point {
    type Output = Self;
    fn add(self, rhs: Point) -> Self::Output {
        Point {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
        }
    }
}

impl Add<i32> for Point { // ✅
    type Output = Self;
    fn add(self, rhs: i32) -> Self::Output {
        Point {
            x: self.x + rhs,
            y: self.y + rhs,
        }
    }
}

fn main() {
    let p1 = Point { x: 1, y: 1 };
    let p2 = Point { x: 2, y: 2 };
    let p3 = p1.add(p2);
    assert_eq!(p3.x, 3);
    assert_eq!(p3.y, 3);
    
    let p1 = Point { x: 1, y: 1 };
    let int2 = 2;
    let p3 = p1.add(int2); // ✅
    assert_eq!(p3.x, 3);
    assert_eq!(p3.y, 3);
}

Note that there are two implementations of Point here.

However, in Swift protocol doesn't support generics, so how can I implement something similar to the snippet above?

If I understand correctly: you want a function that adds a point to another point, and a function that adds a point to a number/scalar, both returning a new point. This should just be a simple function overloading in Swift, without need for protocols.

struct Point {
    let x: Int
    let y: Int
    
    func adding(_ other: Self) -> Self {
        Self(x: self.x + other.x, y: self.y + other.y)
    }
    
    func adding(_ scalar: Int) -> Self {
        Self(x: x + scalar, y: y + scalar)
    }
}

You can also just overload the + operator, too:

extension Point {
    func +(_ lhs: Self, _ rhs: self) {
        lhs.adding(rhs)
    }
    
    func +(_ lhs: Self, _ rhs: Int) {
        lhs.adding(rhs)
    }
}

Thanks for your reply.

In the code snippet I provided, the trait (or protocol in Swift) is just an example.

Maybe I just want to implement some other trait (or protocol) which cannot be replaced by the + operator or any other operator. Are there any approach I can adopt?

Ok I think I understand what you mean now. I think you would like to write something like this in Swift:

protocol Addable<T> {
    associatedtype Output
    func adding(_ addend: T) -> Output
}

struct Point: Addable {
    typealias Output = Self
    let x: Int
    let y: Int
}

extension Addable where Self = Point, T = Point {
    func adding(_ other: Self) -> Self {
        Self(x: self.x + other.x, y: self.y + other.y)
    }
}

extension Addable where Self = Point, T = Int {
    func adding(_ scalar: Int) -> Self {
        Self(x: x + scalar, y: y + scalar)
    }
}

This is currently not legal in Swift, because as you've pointed out, protocols don't support generic parameters. However, even though you can't write it in the same form as you do in Rust, there are often other ways to achieve the same goal, e.g. via function overloading in the concrete type itself like in the example.

Maybe there is a general translation pattern from Rust's generic trait to something in Swift. I don't know much about rust, so maybe someone more familiar with both languages can provide something more in depth.

Do you have some examples where something can only be done via generic parameters on protocols? They can be a motivation for adding generic parameters to protocols or some other language features.

Thanks for your reply.

Here I have an example which can be easily implemented with protocol generics (but I'm sure whether there are elegant implementations in Swift):

Into protocol, used in data conversion

protocol Into<T> {
    func into() -> T
}

struct A {}

struct B {}

struct C {}

extension A: Into<B> {
    func into() -> B {
        B()
    }
}

extension A: Into<C> {
    func into() -> C {
        C()
    }
}

func accept_into_B<T: Into<B>>(_: T) {}

let a = A()
let b: B = a.into<B>() // convert A to B
let c: C = a.into<C>() // convert A to C
accept_into_B(a) // call function

If a struct conforms the Into<T>, it can be convert into T with type by using x.into<T>(). We may want Into<T> to be a protocol (instead implement it in the concrete types) so a function with genercis can accept it.

Swift generally uses the opposite pattern.

So in Rust (from what I understand; I'm only casually familiar with it), you have a value of type X, and you use functions like into_Y() to convert it to a Y. In Swift, we generally have initializers on Y which accept a value of type X.

What's more, if that initializer is generic, X should be a protocol with some semantic meaning, rather than a bag of syntax. "Can be converted in to a Y" is not very interesting in terms of being able to express generic algorithms.

For example, we have the Array<C: Collection>(_: C) initializer, rather than extending every Collection with an into_Array() function. Collection is a protocol with deep semantic meaning; "can be converted in to an Array" is less so.

There may be good use-cases for generic protocols, but personally, whenever I read Rust code, I see a lot of .into()s scattered around, and I find it kind of unreadable. I'm pretty happy we don't use that pattern in Swift.

1 Like

Swift’s lack of generic protocols is an intentional design choice — you can read the reasoning for why they were rejected in full here. In short, generic protocols were rejected because they would likely create confusion with associated types.

If you really want something similar, then you can get pretty close by creating a protocol with an associated type and conforming to that protocol using wrapper types.

protocol Converter {
    associatedtype ConversionResult
    func convert() -> ConversionResult
}

struct Foo {
    struct BarConverter: Converter {
        var foo: Foo
        
        func convert() -> Bar {
            return Bar()
        }
    }
    struct BazConverter: Converter {
        var foo: Foo
        
        func convert() -> Baz {
            return Baz()
        }
    }
    
    // The following properties aren’t really necessary, but they help the code look cleaner.
    var barConverter: BarConverter {
        return BarConverter(foo: self)
    }
    var bazConverter: BazConverter {
        return BazConverter(foo: self)
    }
}
struct Bar {}
struct Baz {}

func doNothing<C: Converter>(with barConverter: C) where C.ConversionResult == Bar {}

let foo = Foo()
let bar = foo.barConverter.convert()
let baz = foo.bazConverter.convert()
doNothing(with: foo.barConverter)

However, this pattern is rarely necessary in real-world Swift code. If you’re having trouble with Swift’s generics model, then this video might help guide you.

I also recommend that you read Swift’s API Design Guidelines if you haven’t already. IMO one of Swift’s biggest advantages over other languages like Rust is its readability, and conforming to these guidelines helps to keep your code clear and concise.

Terms of Service

Privacy Policy

Cookie Policy