Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 267
Alexei Avatar asked Jan 30 '23 19:01

Alexei


2 Answers

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.

like image 108
Rob Napier Avatar answered Feb 01 '23 08:02

Rob Napier


Even though inout parameters modify the variable that was used as an input parameter to the function, they don't exactly work like by reference in other languages. The behaviour in Swift is called copy-in copy-out or call by value result. It means that when you use an inout parameter, at the time of the function call, its value is copied and inside the function, a local copy of the variable is modified. At the time of the functions return, it overwrites the value at the inout parameters original memory location with the modified copy value.

You are printing the address of the variable inside the function, hence you are actually printing the location of the copied value. Try printing after the function returned and you will see that you are printing the original location with the modified value.

For more information, see the In-Out parameters part of the documentation.

like image 24
Dávid Pásztor Avatar answered Feb 01 '23 10:02

Dávid Pásztor