I don’t think so. If you were calling a function g(arg: inout E)
within f
, you’d still have to write g(arg: &arg)
. So the ampersand would still be required in the switch. That said, I agree it would be a very subtle change to remove the ampersand, and then have the code not save the mutation to the inout argument.
These functionality is the one I’m most eagerly expecting. Mutable value semantics is one of the best things about Swift, but sadly it forces you to do some awkward things like use subscripts and forbids using local vars.
I hope that with ‘inout bindings’ we can write more normal looking code while still benefiting from mutable value semantics.
The inout for loops look the most exciting to me, but this seems like a really great improvement in general
Less unsafe pointers
edit: I'm not sure if inout
is a very intuitive declaration keyword though. It makes some sense as a function parameter because it literally says in out; It doesn't behave like a pointer, because to pass it further to yet another function &
must be used again. And if I understand the documentation correctly, inout actually works by copying the argument in and out of the function, it's not a reference, though the compiler can optimise it into a one:
You write an in-out parameter by placing the
inout
keyword right before a parameter’s type. An in-out parameter has a value that’s passed in to the function, is modified by the function, and is passed back out of the function to replace the original value. For a detailed discussion of the behavior of in-out parameters and associated compiler optimizations, see In-Out Parameters.
it's even said explicitly here:
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 behavior is known as copy-in copy-out or call by value result. For example, when a computed property or a property with observers is passed as an in-out parameter, its getter is called as part of the function call and its setter is called as part of the function return.
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. Write your code using the model given by copy-in copy-out, without depending on the call-by-reference optimization, so that it behaves correctly with or without the optimization.
So when I see
inout x = y
I can't imagine what this keyword means. I don't think inout
says mutable reference to me, especially when the keyword is already associated with copying.
This is already guaranteed by SE-0176. inout alias
here is a "read/write access" (in the language of SE-0176) for it's entire lifetime (which is defined in my pitch to extend to the closing }
) and so any access to myStruct
after that point is an exclusivity violation.
That's certainly desirable and I would like to see it in the future but this pitch doesn't currently support this case.
I like this syntax. I'll add it to the pitch as a "Future Direction."
No, that would not be supported. I've been told by the folks implementing this that there is some additional complexity around any kind of conditional binding. I'll explicitly add this to the pitch.
Yes, but I believe the a2
binding here should make a
inaccessible for writing (following SE-0176).
I'd be interested in hearing people's suggestions for alternate names for this keyword.
Tim
Looking forward to this! inout
in particular seems useful for simplifying in-place modification of complex nested collections.
Will optional unwrap conditions using borrow
and inout
support the shorthand introduced in SE-0345? For consistency with let
and var
, I expect we'd want to support this functionality for these new introducers as well.
In the Future Directions of SE-0345, it suggested spellings like this for conditions using the new borrow introducers:
if borrow optional { ... }
if inout &optional { ... }
// shorthand for:
if borrow optional = optional { ... }
if inout optional = &optional { ... }
Resurrecting my previous suggestion:
inout var x = &foo // long form of `inout x = &foo`
inout let y = bar
i still feel that this needs to go where the type annotation usually goes. perhaps we can special case it so that the _
type is not needed:
var x:inout = &foo
inout
before the variable name just doesn’t make sense.
Sorry, I simply don’t agree.
Is this basically about whether inout
refers to the variable binding or the type? That would imply it should go on the left in all cases right?
a long time ago, back when dinosaurs roamed the earth we had:
func foo(var x:T)
func bar(y:inout T)
we got rid of var x:T
because it was too easily confused with y:inout T
. but the principle remains that:
- whether or not something can be mutated by the caller goes to the left of the variable name (
let
,var
). - the effects of that mutation go to the right of the variable name, it is more closely related to the type of the variable than the binding of the variable. nowadays we call this a storage modifier. (
inout
,__shared
,__owned
.)
so i think it would be confusing to put a storage modifier where let
/var
usually goes.
exactly. inout
, like __shared
and __owned
, is actually neither, it goes “in the middle” so to speak.
at any rate this is something that has already been settled, we don’t write func foo(inout x:T)
, we write func foo(x:inout T)
.
Isn't it still conflicting with the existing meaning of inout as a copy, regardless of where it is? Inout here feels logically out of place, like template<class T>
but used with a struct instead of a class.
Why not something that says "I'm a reference" more explicitly?
I'm concerned that this syntax looks far too much like it's "constructing a pointer" and assigning it to a "variable with reference (inout) type." That in turn might lead people to believe they can "rebind x".
var x: inout = &foo
x = &bar // Illegal in the pitch
I agree with @Andrew_Trick that inout
should not imply “no-implicit-copy”. It just means “mutate under an exclusive borrow”. Semantically, this is a copy/modify/writeback, but the copy itself can be elided as long as didSet
s fire for the whole aggregate.
I think I disagree that inout is a storage modifier. It modifies a binding, not the storage. Basically I'm claiming that the existing x: inout T
should always have been inout x: T
.
Had it been that way, how would you spell the type of a function taking an inout
argument? (That said, I'm all fine placing the borrow
or inout
where the let
or var
is.)
today, we don’t really have an issue with:
func foo(x:inout T)
{
var y:T = ...
x = &y // not valid
}
so i don’t see how the new scenario is that different from “rebinding” an inout
function parameter.
it isn’t a ‘real’ storage modifier but it’s been long-established that it goes where the storage modifiers go. to change the language syntax to put inout
before the variable name would be massively source-breaking. and to have a split syntax where the same construct is written one ways in a function signature, and written backwards elsewhere seems like the worst of both worlds.
func foo(inout x: T)
seems logical? I have not spent a lot of time thinking this through, and I just got back from vacation, so it's entirely possible I'm missing something obvious
for the record, i think that you are right, and if this were the swift 3.x days, it might have made more sense to place the inout
before the variable name. but these are not the swift 3.x days, and i think it is more important to be consistent with the existing language syntax.
because regardless of how you feel about where it belongs, changing the positioning of inout
in 2023 is not realistic.
I mean in a type, like (inout Foo) -> Bar
.
ah I see. Good question!