Pitch: `borrow` and `inout` declaration keywords

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. :+1:t2:

5 Likes

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 { ... }

1 Like

Resurrecting my previous suggestion:

inout var x = &foo // long form of `inout x = &foo`
inout let y = bar
1 Like

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.

1 Like

Sorry, I simply don’t agree.

2 Likes

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?

1 Like

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).

1 Like

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
6 Likes

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 didSets fire for the whole aggregate.

2 Likes

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.

6 Likes

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.)

1 Like

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 :slight_smile:

3 Likes

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.

2 Likes

I mean in a type, like (inout Foo) -> Bar.

2 Likes

ah I see. Good question!

1 Like

to spend a few moments in wonderland, we could rationalize this as just saying that we have “deleted” the variable name and the colon, since function types cannot have argument labels.

but this reminds me of spiral const * const * const * const in C, and i always found that very difficult to read.

2 Likes

The tension seems to be that the current inout is a type modifier because it can be nested in function types, but it can't be used anywhere else that a type is expected, so it really feels like a declaration modifier.

3 Likes