Struct mutating temporary var needed?

I have a my own struct Url which stores some Url properties. One is a folders: [String] array.

So I need an append() method to add something to the folders. As it changes the instance, it should be mutating, right?

    public mutating func append(_ string: String) -> Url {
        folders.append(string)
        return self
    }

Now some other class provides some often-used Urls:

        open var urlRoot: Url
	open var urlTemp: Url {urlRoot.append(Self.FOLDER_TEMP)}

Finally, another class needs to generate Urls with even more appended to it. And here is the weirdness. Why do I need a local variable?

    public var url: Url {
        // this works
        var x = Client.current.urlTemp
        return x.append(relative)
        // this does not work
        return Client.current.urlTemp.append(relative)
    }

    // doing it with String struct append, it works without a local var:
    public var test: String {"test"}
    public var test2: String {test.append("2")}

I think the solution here is to mark the function consuming instead of mutating. “mutating” indicates that the caller is likely to need the mutated value, while “consuming” indicates that self is likely not going to be needed again after the call (and that a copy must be made if it is).

1 Like

Are you sure this works? I get two errors, one saying test.append does not return a string, and another saying you can't call a mutating method on test because it is read-only.

It's the same with your Url type: you can only call a mutating method on a mutable variable. Here you are trying to mutate a temporary value returned from a computed property, which does not work because temporary values are immutable. You could call a mutating method if you had a setter for urlTemp because then it would be able to write back the mutated value. But writing back to urlTemp is obviously not what you want here.

Note that all this would work as you expect if Url was a class because a class can be mutated without any write back to the original property.


What you really want here is this:

    public func appending(_ string: String) -> Url {
        var copy = self
        copy.folders.append(string)
        return copy
    }

I removed the mutating and changed the method name to reflect this. No need to mutate your Url in-place when you are going to return a copy.

3 Likes

The why did Swift folks implement Uri as struct? I thought, my Url behaves more like a non-reference object, like a string, than a class. And copy-on-write is what I want when "mutating"/modifying an Url instance.