7 ms·
They implemented what looks like the Rust ownership model: SE-0176 Enforce Exclusive Access to Memory (https://github.com/apple/swift-evolution/blob/master/prop
by sinhpham 9y ago
They implemented what looks like the Rust ownership model: SE-0176 Enforce Exclusive Access to Memory (https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md https://github.com/apple/swift-evolution/blob/master/proposa...), but I'm having a hard time understanding the proposal, can anyone shed some light on this?
- slavapestov 9y agoYou can think of inout parameters in Swift as something analogous to a mutable borrow in Rust. Until Swift 4 we allowed overlapping inout access, for example: var counter = 0 func foo(x: inout Int) { x += 1 print(counter) } foo(x: &counter) Note how 'counter' is read by 'foo(x:)' during an inout access of the same value. This is now prohibited by Swift 4, using a combination of static and dynamic checks. This fixes some undefined behavior and will also enable more aggressive compiler optimizations to be added in the future.
- hellofunk 9y ago> Note how 'counter' is read by 'foo(x:)' during an inout access of the same value It's not clear to me in your example why reading the value of counter after mutating it is bad; why is this now prohibited?
- mikeash 9y agoSwift specifies inout parameters as copying the value that's passed in, giving the copy to the callee as a mutable value, and then writing back the value to the original storage after the function returns. & is not an "address of" operator. Of course, it would be inefficient to do this all the time, so Swift will optimize copy-then-writeback to just passing a pointer to the original storage whenever it can. But this is an optimization operating under the "as if" rule: as long as it works as if it does a copy-then-writeback, the compiler can make it actually do whatever it wants. If the example code were legal, then it would have to print `0`, because the writeback to `counter` doesn't happen until the end of the function. That means the compiler couldn't just pass in a pointer to `counter`, but would have to actually go through the copy-then-writeback procedure it's supposed to do, so you'd lose out on optimizations. Instead, Swift makes it illegal. You can't access a value while this call is happening. That allows the language semantics to coexist with optimizations.
- leshow 9y agowhat does & do in Swift then? And what's the purpose of even having inout and & if you can't count on the code to actually be an address?
- hellofunk 9y agoStill new to Swift but I believe & is an explicit syntax necessary to make clear in the code that the function being called is mutating the argument, and thus the variable passed into that function could be mutated. It must be used wherever an argument is in/out. It makes mutation explicit both in the function declaration and also in the function call. Which is nice! It's good for code readability but also prevents accidentally passing a variable to a function that could mutate it when you weren't expecting that, and vice-versa.
- mikeash 9y ago& is just a sigil saying "I acknowledge that I am passing this as an inout parameter, and therefore the value may be modified by the function I am calling." Note that & is only legal on a function parameter. You cannot write, for example, `let b = &a`. The purpose is to allow for out-parameters. A classic example would be the `+=` operator. (Swift operators are just normal functions with special call syntax.) It takes its first parameter as `inout` so that it can mutate the value. Note that inout parameters work with expressions where it would be impossible to take the address. For example, you can use & on a computed property that has a setter. In that case it has to read the initial value, pass that to the function, then write back the new value, because it has no idea where the computed property actually stores the value, if anywhere. Edit: because I'm obsessive and weird, I made a quick example of this computed property stuff: http://swift.sandbox.bluemix.net/#/repl/59c284376cbea87f72c46a20 http://swift.sandbox.bluemix.net/#/repl/59c284376cbea87f72c4... Click the play triangle at the bottom to see the output.
- leshow 9y ago> (Swift operators are just normal functions with special call syntax.) Coming from Haskell and Rust it's nice to see this trend catching on. Is Swift planning to introduce a distinction between borrows and mutable borrows to the user? From what you describe it seems like right now syntax-wise a borrow and a mutable borrow look the same, and the runtime makes some decision about it. edit: Or I guess it could be the opposite. Since Swift passes by value always unless the runtime can optimize (right?), you could just not write & and inout and cross your fingers it gets optimized to a borrow rather than a copy? Stuff like this makes me prefer the explicitness of Rust. It seems like here on the surface it's abstracted from you, but really you need to know the rules anyway or you could get into trouble.
- leshow 9y agoIsn't the mutable borrow ended after the end of the x += 1 line? leaving you free to read the contents of the ptr? I don't know swift at all. but from their document on swapAt() it looks like they are trying to prevent two fn(&p, &p) where func fn(a: inout Type, b: inout Type)
- hellofunk 9y ago> Isn't the mutable borrow ended after the end of the x += 1 line? No, see Mike's comment here.
- Falell 9y agoNote: I like to read about Rust but don't work with it seriously, and don't follow Swift at all. Corrections welcome. That said, these seemed like the key passages: "Swift has always considered read/write and write/write races on the same variable to be undefined behavior. It is the programmer's responsibility to avoid such races in their code by using appropriate thread-safe programming techniques." "The assumptions we want to make about value types depend on having unique access to the variable holding the value; there's no way to make a similar assumption about reference types without knowing that we have a unique reference to the object, which would radically change the programming model of classes and make them unacceptable for the concurrent patterns described above." Sounds like a system in the vein of rust but more limited, with more runtime checks and no lifetime parameters, falling back to "programmer's responsibility" when things get hard. The last paragraph makes it sound like one of the motivations is in enabling specific categories of optimizations, as opposed to eliminating races at the language level. One of my biggest questions as a reader is how a language like C handles these cases that Swift can't handle without these guarantees. Is this a move to get faster-than-C performance? Does C do these optimizations unsafely? Is there some other characteristic of Swift that makes this harder than C? Closures get a lot of focus in the article...
- slavapestov 9y ago> One of my biggest questions as a reader is how a language like C handles these cases that Swift can't handle without these guarantees. C doesn't address these issues at all as far as I know.
- smitherfield 9y agoC compilers must assume that a function pointer (that is, a function passed as an argument, or that is a property of an object) may write to any global variable. C compilers must also assume that any two pointers to the same type may alias (refer to the same object). The programmer can assert to the compiler that a pointer does not alias any others used in the same scope by declaring it with the `restrict` keyword. For most functions this won't have much effect on the generated code. Writing equivalent functions to the ones in the swift-evolution doc in C, both with and without `restrict` everywhere possible, it looks like `restrict` only has an effect on the generated code for `increaseByGlobal`: https://godbolt.org/g/W8s3BA https://godbolt.org/g/W8s3BA
- pjmlp 9y agoThis is the first step into adopting more Rust like memory safety, but not at the expense of productivity. Basically Swift will keep using reference counting as its GC algorithm, but for high performance situations it will be possible to have a bit more of fine grained control over ownership. However they want to avoid any design that might result in "fighting with borrow checker" feeling. Some info from WWDC 2017, https://developer.apple.com/videos/play/wwdc2017/402/ https://developer.apple.com/videos/play/wwdc2017/402/ There is also a transcript.
- FlyingSnake 9y agoWith Timestamp to the relevant part: https://developer.apple.com/videos/play/wwdc2017/402/?time=2761 https://developer.apple.com/videos/play/wwdc2017/402/?time=2...
- cbcoutinho 9y agoHere's the 'Ownership Manifesto' that tries to clarify some of the differences between the ownership models of Rust and Swift [1]. The main point raised in that document is how 'shared values' are being implemented in Swift in a less strict way compared to Rust. [1] https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md https://github.com/apple/swift/blob/master/docs/OwnershipMan...