amluto
4 hours ago
This seems weak.
In a language like Rust, the compiler will “lock” the pointers for you, and you can’t forget.
In a language like C++ (and presumably Zig), one could, in theory at least, have the iterators and slices that reference the storage of a dynamic array hold some sort of lock that pins the storage.
But this API requires the programmer to remember to lock the pointers and also requires the programmer to keep the lock alive for the correct region of code. And it looks to me like even the example in the blog post has the lock taken completely outside the function that requires stability, so there is nothing whatsoever that gets the lock scoping right. Even the type system can’t help — the offending parse function can’t declare that it wants a pointer-locked ArrayList parameter.
Someone
3 hours ago
I agree. https://news.ycombinator.com/item?id=49501582 says:
“I use it in a lot of places where I know the max capacity ahead of time -- ensureCapacity() followed by a lot of AssumeCapacity()-styled commands. It's convenient for all of the ... convenience ... methods (append() requires some bookkeeping somewhere, appendSlice() requires more, and so on). In those usages, it's basically syntactic sugar over a slice”*
I suspect “where I know the max capacity ahead of time” covers most if not all use cases (if it you use this without knowing max capacity, you either accept your code may panic, or you do some unlock, grow, lock again dance when you discover your initial estimate is wrong)
If so, wouldn’t adding a growable container where you specify capacity at construction time and removing access to the internal pointers of ArrayList be a better way to handle this?
frmdstryr
3 hours ago
Do any languages have a notion of "relative pointers"? So in the example if instead of appending "line" as ptr & len, it'd instead be appending an offset & len which could in theory be used to safely compute the actual location even with relocations.
amluto
an hour ago
Languages with dependent types can express things like “this offset is in bounds relative to this other array”, which is maybe what you’re thinking of.
Someone
2 hours ago
If you squeeze your eyes a bit, C compilers for Windows used to have them, with far pointers (https://en.wikipedia.org/wiki/Far_pointer)
Similarly, CPU architectures that use descriptors can (have to?) have languages with that notion.
frmdstryr
an hour ago
After more searching I found this article https://www.gingerbill.org/article/2020/05/17/relative-point...
A far pointer sounds like the global based pointer described in that article. The far pointer Wikipedia article says they are problematic but doesn't give much reasoning as to why.
sparkie
35 minutes ago
Far pointers are for accessing memory in different segments. They're basically obsolete now. They were necessary in older machines with limited sized pointers or address spaces.
GCC still supports `__seg_fs` and `__seg_gs`, which behave similar to `far` in the example on the wiki page, as the FS and GS segment registers are still valid in x86-64 and used for TLS. Clang uses attributes `address_space(257)` and `address_space(256)` for the same thing.
The `__based` pointer in MSVC exploits the addressing modes by pinning the base in eg: `[base+index*scale+displacement]`. It's unrelated to segmentation.
sparkie
an hour ago
The FS and GS segment selectors are still used in x86-64, typically for `thread_local` storage, but they can be repurposed.
`thread_local` is an example of a "relative pointer" though. Instructions to access the thread local are prefixed with `fs:` or `gs:`, and point relative to the address in the respective segment register.
slopinthebag
9 minutes ago
This is how it is with languages which provide less guarantees than Rust. Sure you can try to hold all the invariants and restrictions in your head, but a sufficiently advanced compiler can do this for you without the possibility of making mistakes. I have no idea why people claim that's too restrictive - if you're not enforcing those rules manually you're just setting yourself up for issues down the road.
pron
3 hours ago
I reach for a low-level language only when I want low-level control over what operations happen and when, what memory is used and when etc.. At present, no language offers me this control and safety at the same time. With Rust, when I need such control (which is always, otherwise I would use a higher-level language), I need to give up safety, anyway, at which point I have no safety and the complexity of a language that offers safety.
So right now, when we want control, we need to give up some safety, but weaker things are still helpful.
Also, in low-level code, the problem of "I might forget to do something" sometimes clashes with the problem of "I need to see exactly what operations are done and where". Various kinds of implicitness help with the former at the expense of the latter.
I'm not saying this is universally better than other approaches, but many people who do serious low-level programming would prefer this.
slopinthebag
7 minutes ago
But the point of unsafe {} in Rust is not that you should never use it, it's that it creates a clear boundary between code that is safe and the code that needs that lower level control. In other languages, everything is inside an unsafe block. If everything you do requires such low level control over every allocation and access, it sounds like you should be using assembly.
xeonmc
an hour ago
Those who would give up low-level control to purchase a little memory safety, deserve neither control nor safety.”
- Benjamin Franklin, or something like thatduped
2 hours ago
What are some examples of things you "always" need that require unsafe Rust?
pjmlp
3 hours ago
Except the point that Zig should do better than Object Pascal, Modula-2, with solutions already available on Insure++ and friends for use after free, 30 years ago.