[swift-users] Understanding pass-by-value

Brent Royal-Gordon brent at architechies.com
Fri Nov 4 08:42:24 CDT 2016


> On Nov 4, 2016, at 5:59 AM, Ryan Lovelett via swift-users <swift-users at swift.org> wrote:
> 
> struct Foo {
>  init(from buffer: Data) {
>     bar = integer(withBytes: Array(buffer[4..<6]))
>     baz = integer(withBytes: Array(buffer[6..<8]))
>     ...
>  }
> 
> let d = Data(count: Int(3e+8))
> let f = Foo(from: d)
> 
> Did I just make two copies of the `Data`? How would I investigate this
> to understand it?

Do you mean, "did I make two copies of the `Data`, one in a top-level variable named `d` and the other in a parameter named `buffer`"?

If so, then answer is "Yes, but…"

A value type like `Data` can't really hold variable-sized data like the bytes in a `Data` object. Instead, the bytes are stored in a separate object, and `Data` manages that with copy-on-write semantics. In other words, there are two copies of the `Data` instance itself, but they share a single copy of the bytes they're storing.

To illustrate more clearly, after this line:

	let d = Data(count: Int(3e+8))

You have something like this:

	| ...stack frame for top level...	|			+-------------------------------+
	| Data instance (d)			| -------------->	 | ...3e+8 bytes of data... |
	| 						|			+-------------------------------+

And then once you execute the call on this line:

	let f = Foo(from: d)

You have this:

	| ...stack frame for top level...	|			+-------------------------------+
	| Data instance (d)			| -------------->	 | ...3e+8 bytes of data... |
	| 						|			+-------------------------------+
	| ...stack frame for Foo(from:)	|					  ^			
	| Data instance (buffer)		| ---------------------------------+

If `Foo(from:)` were to copy `buffer` and then mutate the copy, the bytes would be copied before the mutation occurred. But since neither `f` nor `buffer` is mutated in this code (indeed, both are in immutable variables!), that never happens here.

> I _think_ that if I made it `inout` then it would not make a copy but
> now the data buffer is mutable. I want it to be clear I'm not mutating
> the buffer inside the initializer but I also want to be sure I'm not
> making a copy of the buffer either.

That's implementation-specific. Notionally, an `inout` variable is essentially passed in by value, modified as a copy, returned to the caller, and then assigned back to the original value. In some cases that's basically what actually ends up happening. But Swift tries to optimize `inout` behavior into directly mutating the original variable, and it's often successful.

-- 
Brent Royal-Gordon
Architechies



More information about the swift-users mailing list