Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

434
Views
Why does my struct become immutable in a method chain?

In Swift I am trying to implement a method "tap" similar to the method which exists in Ruby.

I've come up with the following example code:

private protocol Tap {
    mutating func tap(_ block: (inout Self) -> Void) -> Self
}

private extension Tap {
    mutating func tap(_ block: (inout Self) -> Void) -> Self {
        block(&self)
        return self
    }
}

extension Array: Tap {}

var a = Array(repeating: "Hello", count: 5)

a.tap {
    $0.append("5")
}.tap {
    $0.append("7")
}

print(a)  // (Expected) => ["Hello", "Hello", "Hello", "Hello", "Hello", "5", "7"]

I'm not super familiar with mutating functions, inout parameters, or Swift in general, but the code above looks like it should work to me. tap works as expected when it's not being included in a method chain. When I include it as part of a method chain, like in the above example, the Swift compiler complains:

Cannot use mutating member on immutable value: function call returns immutable value

Can anyone explain to me why this doesn't work? Can anyone provide a working solution and explain why that solution works?

Edit:

Another example usage would be:

let user = User(fromId: someId).tap {
   $0.firstName = someFirstName
   $0.lastName = someLastName
}

tap is a convenience thing that comes from Ruby. I'm mainly interested in understanding why the types in my function aren't working out right.

over 4 years ago · Santiago Trujillo
1 answers
Answer question

0

The return self returns a copy of the original array, not the original array itself. Until this copy is stored as a var, it cannot be mutated. So, this would work:

var b = a.tap {
  $0.append("5")
}
b.tap {
  $0.append("7")
}

But not without storing b as a var first. Of course, you wouldn't create a b in the first place, you would just use a repeatedly as you already pointed out.

So, the issue is that you can accomplish tap once, but cannot chain taps. This is because the return of self is implicitly immutable, and you cannot call a mutating function on an immutable value. Changing tap to a non-mutating function could get you what you want:

private extension Tap {
    func tap(_ block: (inout Self) -> Void) -> Self {
        let copy = self
        block(&copy)
        return copy
    }
}

var a = Array(repeating: "Hello", count: 5)

a = a.tap({$0.append("5")}).tap({$0.append("7")})

Because each invocation of tap( returns a copy of the original modified by the given block, you can call it on immutable types. That means that you can chain.

The only downside is that new a = in the beginning.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!