Live data from Hacker News

Swift 5 Exclusivity Enforcement

swift.org

11–20 of 63 posts

Re: Swift 5 Exclusivity Enforcement

#11
post #2

I'm not totally versed in Swift. In the example provided is it ambiguous because the closure captures `count` and count itself is being modified by the method calling the block? I don't understand why this is an example of an unsafe operation. Wouldn't clearly defined behavior of closures clarify the "3 or 4" question?

[deleted]

Re: Swift 5 Exclusivity Enforcement

#12
post #7

Earlier quoted context omitted.

Right. This is one of the things I don't like about automatic reference counting, that it's almost a complete solution, but cycles require more thought (but less cases) to deal with than manual memory management itself.

But how many times the problem truly happened? Is in my experience so rare that the experience is alike with a GC.

- Doubly linked lists.

- Cocoa delegates that keep references to some parent of the objects that they're delegates of.

- Some event listeners (in the same way as delegates).

I wouldn't say it's "rare". It popped up pretty often for me when I was writing Objective C.

Re: Swift 5 Exclusivity Enforcement

#13
post #9

Earlier quoted context omitted.

Right. This is one of the things I don't like about automatic reference counting, that it's almost a complete solution, but cycles require more thought (but less cases) to deal with than manual memory management itself.

It would be interesting to compare the cognitive load of the Swift way and the Rust way. Rust gives you better guarantees, but you must annotate your code.

Rust doesn't just give you better guarantees. It gives you no runtime checking, and potentially even better performance than naive C, because it can automatically tell LLVM when there is no memory aliasing going on.[0] Also, it gives you more confidence, because you can, for example, give out an array to someone and be sure that it won't be modified. And usually that kind of confidence is enough to write safe, race-free parallel code.

I don't think Swift's runtime exclusivity checks give you any of that.

[0]: https://github.com/rust-lang/rust/pull/50744

Re: Swift 5 Exclusivity Enforcement

#14
post #9

Earlier quoted context omitted.

Right. This is one of the things I don't like about automatic reference counting, that it's almost a complete solution, but cycles require more thought (but less cases) to deal with than manual memory management itself.

It would be interesting to compare the cognitive load of the Swift way and the Rust way. Rust gives you better guarantees, but you must annotate your code.

AIUI, Rust can do it "the Swift way" (a misnomer, since it was first) for any given object simply by adding a RefCell or Mutex type constructor, as appropriate. These types are commonly used in combination with reference-counting smart pointers, Rc or Arc. (The smart pointers focus on making the "multiple ownership" aspect work, but this entails that these types have to return shared references, which are ordinarily read-only. The RefCell or Mutex type constructors then focus on enabling "runtime-checked exclusive access" on top of the native, shared reference types).

Re: Swift 5 Exclusivity Enforcement

#15
post #9

Earlier quoted context omitted.

It would be interesting to compare the cognitive load of the Swift way and the Rust way. Rust gives you better guarantees, but you must annotate your code.

Rust doesn't just give you better guarantees. It gives you no runtime checking, and potentially even better performance than naive C, because it can automatically tell LLVM when there is no memory aliasing going on.[0] Also, it gives you more confidence, because you can, for example, give out an array to someone and be sure that it won't be modified. And usually that kind of confidence is enough to write safe, race-f…

Swift arrays have pass-by-value semantics (under the cover, they use copy-on-write to keep performance good) and are thus safe to pass to functions without concern that they'll be modified. (Unless it's passed as an `inout` parameter, which requires a prepended "&" at the call site.)

Re: Swift 5 Exclusivity Enforcement

#16

Earlier quoted context omitted.

Rust doesn't just give you better guarantees. It gives you no runtime checking, and potentially even better performance than naive C, because it can automatically tell LLVM when there is no memory aliasing going on.[0] Also, it gives you more confidence, because you can, for example, give out an array to someone and be sure that it won't be modified. And usually that kind of confidence is enough to write safe, race-f…

Swift arrays have pass-by-value semantics (under the cover, they use copy-on-write to keep performance good) and are thus safe to pass to functions without concern that they'll be modified. (Unless it's passed as an `inout` parameter, which requires a prepended "&" at the call site.)

Rust can use its own bovine superpowers here https://doc.rust-lang.org/std/borrow/enum.Cow.html to enable developers to write code that's invariant over whether it's dealing with a fresh copy of something, or just a borrowed reference. So, it's not quite correct to say that Swift's use of copy-on-write gives it better performance. Rust can express the exact same pattern quite easily, and do it with far more generality.

Re: Swift 5 Exclusivity Enforcement

#18

Earlier quoted context omitted.

Swift arrays have pass-by-value semantics (under the cover, they use copy-on-write to keep performance good) and are thus safe to pass to functions without concern that they'll be modified. (Unless it's passed as an `inout` parameter, which requires a prepended "&" at the call site.)

Rust can use its own bovine superpowers here https://doc.rust-lang.org/std/borrow/enum.Cow.html to enable developers to write code that's invariant over whether it's dealing with a fresh copy of something, or just a borrowed reference. So, it's not quite correct to say that Swift's use of copy-on-write gives it better performance. Rust can express the exact same pattern quite easily, and do it with far more generalit…

I don't mean to imply that Swift's array performance is better than anything other than what its own performance would be if it didn't implement COW. (My intention was to convey that the normal way of passing arrays comes with confidence that your copy won't be modified by the callee.) Rust sounds nice, I'll have to try it sometime.

Re: Swift 5 Exclusivity Enforcement

#20
post #8
post #4

I put Swift into the category of "almost memory managed" languages and it's never fully clicked in my brain. Thinking about strong/weak/unowned references is more difficult in my opinion than when to malloc/free something. Following the examples in the article just reinforced that maybe Swift's approach is a little too complex.

The examples are complex to follow because Swift model works just fine for all the normal cases. It only becomes problematic in some edge cases that regular programmer rarely meet. And with all the help that the compiler now provides (even before swift 5), it's becoming really really hard to shoot yourself in the foot.

I must be specially talented in this area, because I still run into memory issues fairly often. Just a couple days ago, I had a weak reference which was turning nil (Swift weak references are zeroing), and I couldn't figure out why. Both objects were still live. Property observers don't report the nil change, apparently. I couldn't figure out how to get the debugger or Instruments to provide any help, either.

Eventually I just gave up and added an extra 'parent:' argument to an interface, for the one place it was actually needed. It's a bit more awkward than just keeping parent references in the tree nodes, but not too bad.

For situations with multiple queues or threads, the situation is even worse. I wrap a lot of my GCD code in extra locks, which by my reading of the documentation shouldn't be necessary. Without them, it occasionally crashes with strange memory errors that are impossible to figure out.

I felt like I wasted a few hours trying to track down my issue, the other day, and then come up with an alternative solution, while in any other modern language I could just have used a normal reference and counted on the GC to clean up the cycles when I'm done.

Backwards compatibility with Objective-C obviously has tremendous value to Apple, and ARC is smaller and faster than tracing GC, but I feel like I'm paying for it over and over. In most HLLs, once I get past the low-level parts, I'm using a language specifically designed for my task, and I never have to touch the low-level parts again. Swift feels more like fancy C, in that I don't think I'll ever be able to stop thinking about subtle memory management issues.

Post reply on HN