1

I want to check my understanding of how Swift and Rust handle function parameters.

From what I’ve seen, Swift has two main parameter modes:

  • Pass by value, immutable: f(x: T) and
  • Pass by reference, mutable: f(x: inout T).

And, if I recall, Rust has three:

  • Move: f(x: T),
  • Immutable borrow: f(x: &T), and
  • Mutable borrow: f(x: &mut T).

My thinking is that Swift's function signatures map to Rust's borrowing semantics:

  • Swift's f(x: T) behaves like Rust's f(x: &T),
  • Swift's f(x: inout T) behaves like Rust's f(x: &mut T), and
  • Rust's f(x: T) (move) has no Swift equivalent.

The first bullet point is, I assume, the most contentious. In Swift, for most types, f(x: T) passes by value. However, Swift also uses copy-on-write optimizations. And since their arguments are immutable, it should never actually make a copy, right?

func addOne(x: Int) {
    x = x + 1  // Error: Cannot assign to value: 'x' is a 'let' constant
}

Does this analysis seem accurate? Are there edge cases or nuances I'm missing in this comparison between Swift's parameter passing and Rust's ownership/borrowing system?

New contributor
Xander is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

2 Answers 2

1

Whether or not f(x: T) makes copies is unspecified. Swift will make such decisions by itself. From the reference:

By default, Swift uses a set of rules to automatically manage object lifetime across function calls, copying values when required.

f(x: inout T) is specified to make copies (inout literally means "copy in, copy out"), but it is also specified that pass-by-reference can be used as an optimisation.

From the reference

In-out parameters are passed as follows:

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

...

As an optimization, when the argument is a value stored at a physical address in memory, the same memory location is used both inside and outside the function body. The optimized behavior is known as call by reference; it satisfies all of the requirements of the copy-in copy-out model while removing the overhead of copying.

If you want to control the precise behaviour, you can use one of borrowing (similar to a Rust borrow), consuming (similar to a Rust move) to modify the parameters. There are also consume and copy keywords you can apply on expressions to explicitly move or copy a value.

// kind of like 'fn f(x: String, y: &String)'
func f(x: consuming String, y: borrowing String) {
    let a = copy y // explicitly copies 'y' into 'a'
    let b = consume a // moves 'a' into 'b'
    // as a result, adding this line produces an error
    // print(a)
}

Copy-on-write is not relevant to parameters at all. Swift's copy-on-write optimisation refers to how its built-in collection types behave - the storage of the collection is only copied when the collection is modified. e.g.

let a = [1,2,3,4,5]
var b = a // the storage of 'a' is not copied at this line
// 'a' and 'b' now share the same storage

b.append(0) // only at this line, does the storage of 'a' gets copied
// now 'a' and 'b' has separate storages.
0

And, if I recall, Rust has three:

Move: f(x: T),
Immutable borrow: f(x: &T), and
Mutable borrow: f(x: &mut T).

These are not really parameter modes in the sense of swift (or, say, C#). &T and &mut T are types of their own, they're not parameter modifiers, so this really is just one mode: pass by value. It's just that the first one is passing in a T, the second is passing in an &T, and the third one is passing in an &mut T. They could just as well be Arc<T>, MutexGuard<T>, and Cow<'_, T>.

Rust does have mode-ish for methods specifically, because only a limited number of types are allowed there and some of them have a shorthand syntax (self, &self, &mut self) and which is used is not necessarily explicit at the callsite: https://doc.rust-lang.org/reference/items/associated-items.html#methods

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.