# Borrowing still creates a copy of the method parameter?!

**URL:** <https://forums.swift.org/t/borrowing-still-creates-a-copy-of-the-method-parameter/82291>\
**Category:** Using Swift\
**Tags:** ownership, borrowing\
**Created:** [September 22, 2025, 8:31pm UTC](https://forums.swift.org/t/borrowing-still-creates-a-copy-of-the-method-parameter/82291 "2025-09-22T20:31:05Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![winstondu](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/winstondu/32/42925_2.png) [@winstondu](https://forums.swift.org/u/winstondu)\
**Post date:** [September 22, 2025, 8:31pm UTC](https://forums.swift.org/t/borrowing-still-creates-a-copy-of-the-method-parameter/82291/1 "2025-09-22T20:31:05Z")

</div>

I’m trying to build an intuition for the parameter ownership modifiers—specifically borrowing—as described in SE-0377. My mental model (possibly wrong!) is that a borrowing parameter gives the callee temporary, read-only access to the value without taking ownership, which (for copyable types) _ **should\*** _ mean the callee avoids making a copy of the parameter if it is a struct.

The small example below (a `Foo` struct wrapping a `_Storage` class with checks via `isKnownUniquelyReferenced`) surprised me. I pass an instance `foo` into the method `fast(foo: borrowing Foo)`, then—before `fast` returns—on another queue I check `isKnownUniquelyReferenced(&foo.storage)` and observe **“copy detected”**.

Since a second strong reference to my storage class instance appeared, this suggests a struct copy, contrary to my expectation that `borrowing` would behave like “pass by reference”.

```swift
final class _Storage {
    var x: Int
    init(_ x: Int) { self.x = x }
}

struct Foo {
    private var storage: _Storage
    init(_ x: Int) { storage = _Storage(x) }
    var x: Int { storage.x }

    mutating func ensureUnique() {
        if isKnownUniquelyReferenced(&storage) {
            print("No copy for now")
        } else {
            print("Copy detected")
        }
    }

    mutating func setX(_ new: Int) {
        ensureUnique()
    }
}

func fast(foo: borrowing Foo) {
    Thread.sleep(forTimeInterval: 2.0)
    print(foo.x)
}

```

My calling code:

```swift
var foo = Foo(1) // 1. Create Foo
foo.ensureUnique() // 2. Verify there is only one Foo --> "No copy for now"
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
    foo.ensureUnique() // 4. Check while fast() is running --> A copy is detected!
}
fast(foo: foo) // 3. Start fast() method -- foo is passed -- is it by pass by ref or pass by value?

```

A potential confounder I’m considering and would love help ruling in/out:

- **Caller-side retain/copy during a borrow.** Even with borrowing, the call site may legally create a temporary/retain so the argument stays alive while borrowed; that would make `isKnownUniquelyReferenced` report false during the overlap. (Joe Groff notes in this reply [here](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020/13) that caller copies/retains are an orthogonal decision, even for borrowed params!) How can I structure a demo that _proves_ no extra owner is introduced at the call boundary?

If my expectation is off:

1. What’s the precise, _ **checkable** _ statement I should be testing to understand the behavior of borrowing?  
For example, is the right mental model: “borrowing guarantees the callee won’t implicitly copy, but the caller may still retain/copy to satisfy exclusivity and lifetime rules”?  
– If so: Any minimal example (or SIL/IR inspection tips for a N00b) that demonstrates the intended semantics—ideally one that avoids these confounders—would be greatly appreciated.
2. How can I get the performance benefit similar to C++’s pass by const reference to avoid copying large structs? Would I have to implement Copy-On-Write for my custom structs using something like CowBox?

---

<div class="post-metadata">

**Author:** ![bbrk24](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/bbrk24/32/27370_2.png) [@bbrk24](https://forums.swift.org/u/bbrk24)\
**Post date:** [September 22, 2025, 8:56pm UTC](https://forums.swift.org/t/borrowing-still-creates-a-copy-of-the-method-parameter/82291/2 "2025-09-22T20:56:28Z")

</div>

> [@winstondu](#):
>
> My calling code:
> 
> ```swift
> var foo = Foo(1) // 1. Create Foo
> foo.ensureUnique() // 2. Verify there is only one Foo --> "No copy for now"
> DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
> foo.ensureUnique() // 4. Check while fast() is running --> A copy is detected!
> }
> fast(foo: foo) // 3. Start fast() method -- foo is passed -- is it by pass by ref or pass by value?
> 
> ```

Based on just that snippet, I would think it's the `asyncAfter` that's making a copy.

---

<div class="post-metadata">

**Author:** ![winstondu](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/winstondu/32/42925_2.png) [@winstondu](https://forums.swift.org/u/winstondu)\
**Post date:** [September 22, 2025, 9:06pm UTC](https://forums.swift.org/t/borrowing-still-creates-a-copy-of-the-method-parameter/82291/3 "2025-09-22T21:06:56Z")

</div>

That `asyncAfter` could have made a copy was my initial hunch (and also what chatgpt was gaslighting me about), but it was fairly easy to rule out with this small change.

```swift
var foo = Foo(1) // 1. Create Foo 
foo.ensureUnique() // 2. Verify there is only one Foo --> This prints "No copy for now" 
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) { 
    foo.ensureUnique() // 4. Check for copy
} 
Thread.sleep(forTimeInterval: 2.0) // 3. Sleep

```

This yields:

```swift
No copy for now
No copy for now

```

The difference indicates it the copying comes directly from the `fast` method call.

---

<div class="post-metadata">

**Author:** ![j-f1](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/j-f1/32/12287_2.png) [@j-f1](https://forums.swift.org/u/j-f1)\
**Post date:** [September 22, 2025, 10:06pm UTC](https://forums.swift.org/t/borrowing-still-creates-a-copy-of-the-method-parameter/82291/4 "2025-09-22T22:06:22Z")

</div>

That example isn’t quite the same, because you’re not keeping a reference to `foo` alive in the outer scope. If you add `print(foo)` to the bottom, you should see a copy occurring once again.

---

<div class="post-metadata">

**Author:** ![RandomHashTags](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/randomhashtags/32/24259_2.png) [@RandomHashTags](https://forums.swift.org/u/RandomHashTags)\
**Post date:** [September 22, 2025, 10:24pm UTC](https://forums.swift.org/t/borrowing-still-creates-a-copy-of-the-method-parameter/82291/5 "2025-09-22T22:24:45Z")

</div>

> [@winstondu](#):
>
> My mental model (possibly wrong!) is that a borrowing parameter gives the callee temporary, read-only access to the value without taking ownership,

I think that is correct. The problem I think you're encountering is the subtle difference between noncopyable **values** and copyable ones. If I'm not mistaken, the default modifier for passing a copyable value is `borrowing`, so it doesn't need to be explicitly declared. Declaring it probably does nothing or gives a hint (not enforced) to the compiler that it should only be borrowed, but it doesn't make the copyable value noncopyable.

You need to explicitly make your `struct` conform to `~Copyable` to fully utilize the ownership modifiers (and possibly conform your types to `Sendable`).

[Here](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0390-noncopyable-structs-and-enums.md) is the proposal for noncopyables.

---

<div class="post-metadata">

**Author:** ![winstondu](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/winstondu/32/42925_2.png) [@winstondu](https://forums.swift.org/u/winstondu)\
**Post date:** [September 22, 2025, 11:20pm UTC](https://forums.swift.org/t/borrowing-still-creates-a-copy-of-the-method-parameter/82291/6 "2025-09-22T23:20:20Z")

</div>

```swift
var foo = Foo(1) // 1. Create Foo
foo.ensureUnique() // 2. Verify there is only one Foo --> This prints "No copy for now"
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
    foo.ensureUnique() // 4. Check for Copy
}
Thread.sleep(forTimeInterval: 2.0) // 3. Sleep
print(foo.x) // 5. Print foo

```

Yields:

```swift
No copy for now
No copy for now
1

```

In other words, no copy.

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [September 22, 2025, 11:21pm UTC](https://forums.swift.org/t/borrowing-still-creates-a-copy-of-the-method-parameter/82291/7 "2025-09-22T23:21:29Z")

</div>

Swift will conservatively copy copyable values in some situations even when passing to a `borrowing` parameter. Otherwise, it would be source-breaking for callers if a function adopted `borrowing`.

We do want to make Swift better about avoiding these conservative copies when they can easily be proven semantically unnecessary. Your example code, of course, has a data race in it, and while the conservative copy doesn't actually fix this — the data race is innate — it does make observable misbehavior less likely in practice.

---

<div class="post-metadata">

**Author:** ![winstondu](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/winstondu/32/42925_2.png) [@winstondu](https://forums.swift.org/u/winstondu)\
**Post date:** [September 22, 2025, 11:29pm UTC](https://forums.swift.org/t/borrowing-still-creates-a-copy-of-the-method-parameter/82291/8 "2025-09-22T23:29:23Z")

</div>

John, thank you for taking the time to elaborate. My example code, of course, **intentionally** data-raced in order for me observe/verify the underlying behavior.

Could you suggest a better way of observing some behaviors of `borrowing` in code – especially whether a copy is made (without having to look at perf/SIL/AST)?

> [@John\_McCall](#):
>
> it would be source-breaking for callers if a function adopted `borrowing`.

^ I’m not sure if I follow. If I change the signature of a method in my library, I think users of my library will agree that it is fair game for me to break their code.

In addition, could you address:

> [@winstondu](#):
>
> How can I get the performance benefit similar to C++’s pass by const reference to avoid copying large structs? Would I have to implement Copy-On-Write for my custom structs using something like CowBox?

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [September 22, 2025, 11:38pm UTC](https://forums.swift.org/t/borrowing-still-creates-a-copy-of-the-method-parameter/82291/9 "2025-09-22T23:38:32Z")

</div>

> [@winstondu](#):
>
> Could you suggest a better way of observing some behaviors of `borrowing` in code – especially whether a copy is made (without having to look at perf/SIL/AST)?

No, we don't have great tools for observing this right now.

> [@winstondu](#):
>
> ^ I’m not sure if I follow. If I change the signature of a method in my library, I think users of my library will agree that it is fair game for me to break their code.

Understood, but we didn't want to consider adding `borrowing` to be a signature change in that way, because that would prevent it from being used to get the explicit-copies semantics in the callee.

> [@winstondu](#):
>
> How can I get the performance benefit similar to C++’s pass by const reference to avoid copying large structs? Would I have to implement Copy-On-Write for my custom structs using something like CowBox?

The most reliable mechanism in the language as it stands would be to just make the type non-copyable and provide a `copy()` method. I understand that this isn't necessarily a great solution; the team is looking at providing better support for this in the language.

---

<div class="post-metadata">

**Author:** ![winstondu](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/winstondu/32/42925_2.png) [@winstondu](https://forums.swift.org/u/winstondu)\
**Post date:** [September 22, 2025, 11:49pm UTC](https://forums.swift.org/t/borrowing-still-creates-a-copy-of-the-method-parameter/82291/10 "2025-09-22T23:49:55Z")

</div>

Would you be able to illustrate the copy method approach with a toy example?

My confusion with noncopyable structs is if I have multiple methods in succession that takes in the struct as a parameter, isn’t that just consuming?

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [September 22, 2025, 11:55pm UTC](https://forums.swift.org/t/borrowing-still-creates-a-copy-of-the-method-parameter/82291/11 "2025-09-22T23:55:31Z")

</div>

The fact that it's used multiple times does not by itself require it to be consumed, no. Only if one of the uses (necessarily the last) is `consuming` does it need to be consumed.

> [@winstondu](#):
>
> Would you be able to illustrate the copy method approach with a toy example?

```swift
struct X: ~Copyable {
  var value: Int
  func copy() -> X {
    return X(value: value)
  }
}

```
