2

How can I pass a constant parameter by reference?

In Swift it can be done just for variables (and using inout keyword).

Can I pass a constant by reference?

I want to do something like this:

let x = ["a":1, "b":2]
myFunc(&x)
vadian
  • 274,689
  • 30
  • 353
  • 361
rick
  • 1,009
  • 2
  • 10
  • 28
  • *I want to do something like this:* Why? You never said why you want to do that. – vacawama Nov 05 '17 at 13:54
  • Because `x` is very large. – rick Nov 05 '17 at 13:58
  • So you're concerned that it is inefficient to pass a copy of `x` to the function? Swift only copies on write, so the function will receive the original dictionary anyway. – vacawama Nov 05 '17 at 14:01
  • But I don't want to modify `x`. – rick Nov 05 '17 at 14:19
  • So you mean by default will `x` be passed by reference? But I think dictionaries will be passed by value, because the are not class. Am I right? – rick Nov 05 '17 at 14:28
  • 1
    You're right. Dictionaries are structs and thus passed by value which implies that a copy is made. Behind the scenes, Swift will do the efficient thing and not create a second copy of the dictionary. – vacawama Nov 05 '17 at 14:43
  • 1
    @rick Being a reference or a value type is orthogonal to whether it is passed by reference or by value. Generally, arguments in Swift are always passed by value, unless passed with `&` (such as can be done with `inout` and `UnsafeMutablePointer` params). If you have a `[String: Int]` parameter, the dictionary you pass to it is passed by value. However, as I said earlier, `Dictionary` stores its keys + values indirectly in a reference-counted buffer (actually 2 buffers for native). – Hamish Nov 05 '17 at 15:55
  • 1
    Simply passing a `Dictionary` by value does *not* copy those underlying buffers (only the pointers to those buffers!). Both the caller and the callee have a view onto the *same* buffers (they will take their own copy on a mutation taking place though – that's called copy-on-write). For this reason, you really don't need to pass the dictionary by reference. – Hamish Nov 05 '17 at 15:55
  • I think I need more study. I know what are reference-counting and copy-on-write, but I didn't get you. – rick Nov 06 '17 at 06:59
  • Thanks. I get you. You are so helpful. – rick Nov 07 '17 at 06:11

2 Answers2

2

You cannot pass a let constant to an inout parameter.

According to Swift reference

In-out parameters are passed as follows:

  • When the function is called, the value of the argument is copied.
  • In the body of the function, the copy is modified.
  • When the function returns, the copy’s value is assigned to the original argument.

This automatically disqualifies let constants, because they cannot be assigned back.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Thanks. Yes I know. So Swift cannot do it, yes? – rick Nov 05 '17 at 13:35
  • @rick Correct. They even removed `var` in Swift 3, which killed a potentially useful case of caller-side mutation. – Sergey Kalinichenko Nov 05 '17 at 13:38
  • @dasblinkenlight You mean `var` parameters specifically, right? They didn't actually allow for caller-side mutation though, it was merely a mutable copy for the callee (in fact one of the reasons it was removed was due to being confused with the caller-side mutation behaviour of `inout`). – Hamish Nov 07 '17 at 02:05
2

Can I pass a constant by reference?

Not that I'm aware of. However, you should never need to do so yourself.

In the specific case you raise with a Dictionary, you're dealing with a collection that stores its contents indirectly. At its simplest level, you can think of it as a structure that just has a single property, which is merely a reference to the actual storage for the keys & values of the dictionary.

Passing that struct, the dictionary, by value is equivalent to just passing a reference about. Multiple dictionaries can view the same key & value storage through that reference. However, a unique copy of the storage will be taken on mutation if necessary – this is called copy-on-write, and allows for value semantics without the overhead of copying the entire buffer every time the dictionary structure is copied.

So for that reason, there is absolutely no need for a pass-by-reference here. Just pass the dictionary normally (i.e by value – that is, not with & such as with inout and pointer parameters).

In the more general case of having a large structure that you want to avoid copying when passing by value to a function – the compiler already has you covered. It can perform various clever optimisations to reduce copying; one of which is passing the structure by reference to a function. I go into more detail about this in this Q&A.

It's really not something you should need to think about unless:

  • You actually need caller-side mutation (in which case, you want a var anyway).

  • You've specifically identified a performance bottleneck (in that case, you may well benefit from making your own copy-on-write structure).

Hamish
  • 78,605
  • 19
  • 187
  • 280