Inout parameters but `inlined`

Hi, first post here and n00b to Swift programming.

I started using inout parameters to try and solve an issue I'm working on, and I was wondering if it's possible to do this, or if maybe this is in discussion to have implemented.

Is there a way to get around having to create a temporary stray variable that has to be passed into a function that accepts an inout param?

So instead of

var value: Double
tryParse("1337", &value)

Maybe it would have been something like

// similar to how `out` can be used in C#
tryParse("1337", var value: Double) 

Just an example of what I would do in C#

if(int.TryParse(GetSomeConfigurationString(), out int result) && result > 100)
{
}

I know this is probably just syntactic sugar, however as I'm learning more and more about Swift I'm getting the feeling that the sugar made here is a lot healthier than that in other programming languages.

I call a fair number of C APIs that return a pointer and have an “out count” param. This would save me a line, but I don’t think that’s all that appealing on its own.

What would be great is if this allowed your variable to be let instead of var

1 Like

Though just to be clear: C# uses a way more out parameters than Swift. In particular, it uses it on functions that can fail, where the return value is a bool that can be used directly in an if statement.

This is used as a makeshift Maybe monad. In Swift, we have a real one: Optional<T>, which has a first class syntax for conditional unwrapping.

That same code would be written in idiomatic Swift like:

if let result = Int(getSomeConfigurationString()), result > 100 {
    // ...
}

Which calls a failable initializer of Int (Int.init?(_ description: String)), which has type (String) -> Int?.

6 Likes

Is result available inside the body of "if" in C# version?

Here you go:

var value = 0.0; tryParse("1337", value: &value)

A few other options:

  • if that's your API - just change it so it returns the value vs using out parameter.
    (might be related to your case - consider making the func throwing)
  • if it's existing (say, C API) - try passing nil in there - maybe it can accept it.
  • if it's a single thread only case - pass some "&Double.scratchValue" which you declare once elsewhere.
  • otherwise don't try to save this line, not a big deal

Having said that, actual out parameters would be really nice for situations that would otherwise require temporary uninitialized buffers.

Not only is the result available within the the if statement’s body, but it’s also available within the rest of the predicate expression, to be immediately checked by some comparison.

1 Like

You can abstract out this pattern:

public func with<Value>(_ value: Value, do body: (_ value: inout Value) throws -> ()) rethrows -> Value {
    var value = value
    try body(&value)
    return value
}

And use it like this:

let value = with (0.0) { tryParse("1337", &$0) }

Or like this:

if with(0.0, do: { tryParse("1337", &$0) }) > 100 {
   ...
}

@mayoff
How well would this code read in the event you had multiple inout parameters?

I'm starting to think that the cleanest way it's going to get right now is to write the code the old 'pinvoke' way.

I remember having to deal with some methods that would return two out parameters, and at the time I didn't really care much that I had to declare two placeholder variables, but once I was able to inline my out calls, it felt so much cleaner and readable, and the fact that the parameters were scoped as if I had written it as a stray variable sold me on the idea.

I ended up doing what @AlexanderM suggested, the optional return within a guard let or if let. Maybe this will get built into Swift one day, I know that its just an extra line right now that's being saved, but it almost seems out of place coming from an environment that supports this already.

We already have guard let which properly scopes variables from its conditional.

This is the correct call (and I'm not just saying that because I suggested it).

If I conuted all the functions I've defined with inout params (not counting mutating func which is technically inout applied to self`) in my projects, the median number is 0. Returning an optional is simply a better pattern.