Covariance for ` inout ` parameters missing?

Here’s some toy code demonstrating an issue I’ve run into. It’s a simple bank account model:


protocol Account {
    var balance: Double { get }
    mutating func deposit(amount: Double)
    mutating func withdraw(amount: Double)
    
    init(initialAmount: Double)
    
    mutating func transfer(to account: inout Account, amount: Double)
}

extension Account {
    
    mutating func transfer(to account: inout Account, amount: Double) {
        withdraw(amount: amount)
        account.deposit(amount: amount)
    }
}


struct EconomyAccount: Account {
    var balance: Double
    init(initialAmount: Double) {
        balance = initialAmount
    }

    mutating func deposit(amount: Double) {
        balance += amount
    }

    mutating func withdraw(amount: Double) {
        balance -= amount
    }
    
}

var account = EconomyAccount(initialAmount: 20)
account.deposit(amount: 100)
account.balance

var account2 = EconomyAccount(initialAmount: 30)

account.transfer(to: &account2, amount: 300) // Error: EconomyAccount is not Account

account2 is a subtype of the Account type however the transfer() function doesn’t allow me to pass it in. Implying that the parameter type is invariant.

However I can somewhat workaround this by making a copy of the variable:

var account2 = EconomyAccount(initialAmount: 30)
var cpy:Account = account2 // Copy to type Account
account.transfer(to: &cpy, amount: 300)

This workaround seems like something that would be done implicitly via passing a subtype into a parameter.

In addition this works for inout params where the type is generic. Which as a result is covariant:

mutating func transfer<T: Account>(to account: inout T, amount: Double)

This leads me to ask if there is a specific reason that swift doesn’t have covariance for inout parameters as is, or is this a feature being worked on.

Any input would be appreciated, Thanks

Imagine if instead of

    mutating func transfer(to account: inout Account, amount: Double) {
        withdraw(amount: amount)
        account.deposit(amount: amount)
    }

you had

    mutating func transfer(to account: inout Account, amount: Double) {
        account = SomeOtherTypeThatImplementsAccountButClearlyIsntAnEconomyAccount()
    }

It would try to assign SomeOtherTypeThatImplementsAccountButClearlyIsntAnEconomyAccount to a variable of type EconomyAccount which would be bad

Ok thank you that helped a lot. I wasn’t paying attention to the variable assignment as a result of inout, which was my error.

Terms of Service

Privacy Policy

Cookie Policy