inout func parameters

Hi


I'm trying to figure out the following gives me an error.


protocol SomeProtocol
{
    var name: String
    {
        get
        set
    }
}

class SomeName : SomeProtocol
{
    public var name: String
    
    init()
    {
        self.name = ""
    }
    
    init(name: String)
    {
        self.name = name
    }
    
}

class SomeContext
{
    private init()
    {
    }

    open static let shared = SomeContext()
    
    public func someMethod(with value: inout T, age: [Int])
    {
        print("before: \(value.name)")
        value.name = "foo"
        print("after: \(value.name)")
        
        print("age: \(age)")
    }
}

func createSomeName()
{
    var value = SomeName(name: "Fred")
    testInout(value: &value)
    print(value.name)
}

func testInout(value: inout SomeProtocol)
{
    SomeContext.shared.someMethod(with: &value, age: [1,2,3])
}

createSomeName()


On line 47, I get this error:

error: cannot pass immutable value as inout argument: implicit conversion from 'SomeName' to 'SomeProtocol' requires a temporary

testInout(value: &value)


On line 53, I get this error:

error: in argument type 'inout SomeProtocol', 'SomeProtocol' does not conform to expected type 'SomeProtocol'

SomeContext.shared.someMethod(with: &value, age: [1,2,3])


Basically I need to be able to pass class/or struct to a method, which passes it to another method which can modify the value i.e name. I tried using a struct but ended up with the same errors.


If I replace line 47 with line 53, it seems to work fine with either a struct or class


Any ideas how to work around this problem?


Thanks

Accepted Reply

Something munged your code listing so that it doesn’t compile. I’ve fixed it by presuming that line 34 should be this:

public func someMethod<T : SomeProtocol>(with value: inout T, age: [Int])

Let me know if that’s incorrect.

The error on line 47 is because the

inout
protocol parameter could change the concrete type of the value. That is, you could pass it a value of
SomeName
and get back a value of
SomeOtherName
, a different concrete type that conforms to
SomeProtocol
. There’s two ways to fix this:
  • If you only care that

    value
    conforms to
    SomeProtocol
    , change line 46 to this:
    var value = SomeName(name: "Fred") as SomeProtocol

    .

  • Alternatively, you could write something like this:

    func createSomeName()
    {
        var value = SomeName(name: "Fred")
        var t = value as SomeProtocol
        testInout(value: &t)
        print(value.name)
    }

    and then deal with the fact that

    t
    might come back as a different type then
    value
    .

    Note that

    t
    is the “temporary” mentioned by the error.

The error on line 53 is because

someMethod(…)
is expecting a concrete type that conforms to
SomeProtocol
, but
testInout(…)
doesn’t have a concrete type, it has a value of the protocol type. You don’t need
inout
to trigger this issue; the following code has the same problem:
protocol MyProtocol { }

func callee<T: MyProtocol>(value: T) { }

func caller(value: MyProtocol) {
    callee(value: value)
    // Cannot invoke 'callee' with an argument list of type '(value: MyProtocol)'
}

The most obvious fix here is to make

testInout(…)
also generic:
func testInout<T2 : SomeProtocol>(value: inout T2)
{
    SomeContext.shared.someMethod(with: &value, age: [1,2,3])
}

Now

T
is a concrete type that conforms to
SomeProtocol
, so when you call
someMethod(…)
you’re calling it with a concrete type (albeit a generic one).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

Something munged your code listing so that it doesn’t compile. I’ve fixed it by presuming that line 34 should be this:

public func someMethod<T : SomeProtocol>(with value: inout T, age: [Int])

Let me know if that’s incorrect.

The error on line 47 is because the

inout
protocol parameter could change the concrete type of the value. That is, you could pass it a value of
SomeName
and get back a value of
SomeOtherName
, a different concrete type that conforms to
SomeProtocol
. There’s two ways to fix this:
  • If you only care that

    value
    conforms to
    SomeProtocol
    , change line 46 to this:
    var value = SomeName(name: "Fred") as SomeProtocol

    .

  • Alternatively, you could write something like this:

    func createSomeName()
    {
        var value = SomeName(name: "Fred")
        var t = value as SomeProtocol
        testInout(value: &t)
        print(value.name)
    }

    and then deal with the fact that

    t
    might come back as a different type then
    value
    .

    Note that

    t
    is the “temporary” mentioned by the error.

The error on line 53 is because

someMethod(…)
is expecting a concrete type that conforms to
SomeProtocol
, but
testInout(…)
doesn’t have a concrete type, it has a value of the protocol type. You don’t need
inout
to trigger this issue; the following code has the same problem:
protocol MyProtocol { }

func callee<T: MyProtocol>(value: T) { }

func caller(value: MyProtocol) {
    callee(value: value)
    // Cannot invoke 'callee' with an argument list of type '(value: MyProtocol)'
}

The most obvious fix here is to make

testInout(…)
also generic:
func testInout<T2 : SomeProtocol>(value: inout T2)
{
    SomeContext.shared.someMethod(with: &value, age: [1,2,3])
}

Now

T
is a concrete type that conforms to
SomeProtocol
, so when you call
someMethod(…)
you’re calling it with a concrete type (albeit a generic one).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"