Search code examples
swiftswift3memory-address

Passing variables by reference in Swift


I started learning c++ and now I am wondering if I can do some things in Swift as well.

I never actually thought about what happens when we pass a variable as an argument to a function in Swift.

Let's use a variable of type string for examples.

In c++ I can pass an argument to a function either by making a copy of it, or by passing a reference/pointer.

void foo(string s) or void foo (string& s); In the 1st case the copy of my original variable will be created, and foo will receive a copy. In the 2nd case, I basically pass an address of the variable in memory without creating a copy.

Now in Swift I know that I can declare an argument to a function to be inout, which means I can modify the original object.

1) func foo(s:String)... 2) func testPassingByReference(s: inout String)...

I made an extension to String to print the address of the object:

extension String {
    func address() -> String {
        return String(format: "%p", self);
    }
}

The result was not that I expected to see.

var str = "Hello world"
print(str.address()) 

0x7fd6c9e04ef0

func testPassingByValue(s: String) {
    print("he address of s is: \(s.address())")
}
func testPassingByReference(s: inout String) {
    print("he address of s is: \(s.address())")
}

testPassingByValue(s: str)

0x7fd6c9e05270

testPassingByReference(s: &str)

0x7fd6c9e7caf0

I understand why the address is different when we pass an argument by value, but it's not what I expected to see when we pass an argument as an inout parameter.

Apple developer website says that

In Swift, Array, String, and Dictionary are all value types.

So the question is, is there any way to avoid copying objects that we pass to functions (I can have a pretty big array or a dictionary) or Swift doesn't allow us do such things?


Solution

  • Copying arrays and strings is cheap (almost free) as long as you don't modify it. Swift implements copy-on-write for these collections in the stdlib. This isn't a language-level feature; it's actually implemented explicitly for arrays and strings (and some other types). So you can have many copies of a large array that all share the same backing storage.

    inout is not the same thing as "by reference." It is literally "in-out." The value is copied in at the start of the function, and then copied back to the original location at the end.

    Swift's approach tends to be performant for common uses, but Swift doesn't make strong performance promises like C++ does. (That said, this allows Swift to be faster in some cases than C++ could be, because Swift isn't as restricted in its choice of data structures.) As a general rule, I find it very difficult to reason about the likely performance of arbitrary Swift code. It's easy to reason about the worst-case performance (just assume copies always happen), but it's hard to know for certain when a copy will be avoided.