News for hackers. Stuff that matters.
Re:You can't Rust that by oconnor663 on Saturday March 31, 2018 @04:16PM (#16724965)
> I thought rust would want to reduce cache churn

I think the main way Rust reduces cache churn, is that it makes it safer to pass references/pointers around instead of making copies. For example, if I have f(char*) in C, I might be worried about what f is doing with that pointer. It might save it somewhere, or try to free it, or who knows what. In C++, I might prefer to write f(std::string) to avoid those worries, but that comes at the cost of unnecessarily copying the string. (You could pass it by reference to avoid the copy, but then you still have to worry about f saving the reference.) In Rust, I can safely write f(&str), and I know f can't do anything dirty with that reference.

Re:You can't Rust that by jstimpfle on Saturday March 31, 2018 @04:46PM (#16725099)
> If I have f(char*) in C, I might be worried about what f is doing with that pointer. It might save it somewhere, or try to free it, or who knows what

FWIW my assumption always is that it's "a borrow". I.e., the called function should not make any assumptions how the memory was allocated, what is its lifetime, or even try to free it or keep it in an associated datastructure that persists the lifetime of the function. The only thing it should normally assume is that it points to valid memory.

In my experience the instances where ownership moves across function boundaries are far and far between, and can easily be documented, often simply by giving it an appropriate name.

Re:You can't Rust that by the_mitsuhiko on Saturday March 31, 2018 @04:48PM (#16725109)
> I.e., the called function should not make any assumptions how the memory was allocated, what is its lifetime

It needs to at least make the assumption that the memory will not be invalidated while the function did not return. In the presence of multi threading that in itself might already be surprisingly hard to guarantee sometimes. Also typically functions make assumptions about the immutability of things in such cases which are often just not true.

Re:You can't Rust that by jstimpfle on Saturday March 31, 2018 @05:40PM (#16725313)
Problems with multithreading and "perceived immutability" are two more things that can largely be ruled out with a clear dataflow architecture. For example, I try to not have "cycles" such that a function call overwrites a memory location that was a (pointer) argument to the caller. I don't think that should happen - where did the called function get the pointer from without the caller knowing?

An architecture with global data tables instead of OOP helps here, because it has much lower need for pointer arguments of unclear origin. I also agree very much with your approach "handles instead of pointers" (if by handles you mean integer array indices). Indices for example trivially survive reallocation of a dynamically growable array.

Re:You can't Rust that by amarkov on Sunday April 01, 2018 @02:15PM (#16729642)
Sure, but isn't that the point for Rust? You can rule out huge classes of problems with a clear dataflow architecture - thus, the language should enforce one, rather than requiring programmers to hand-roll one correctly.
Re:You can't Rust that by jstimpfle on Sunday April 01, 2018 @10:26PM (#16731847)
I've never used Rust, but my impressionn is it's quite constraining... The syntax alone turns me off.
Re:You can't Rust that by klmr on Monday April 02, 2018 @06:56AM (#16733570)
> FWIW my assumption always is that it's "a borrow".

But for how long? Forget about multithreading for a moment, but can my function store the pointer for later use? This is incredibly common in OOP, and dangling pointers/references are one of the (if not the) top sources of bugs in C and C++. And unlike other sources of bugs, there’s no good fundamental remedy against it.

Re:You can't Rust that by jstimpfle on Monday April 02, 2018 @07:25AM (#16733672)
> But for how long?

As I wrote, "lifetime of the function". Maybe that was poorly worded - of course I mean "until the function call returns". (I think "borrow" is universally understood in this way).

That means, again, don't assume you are allowed to keep the pointer argument in an associated datastructure. In general we don't know where the data is coming from, how it was allocated, how or when it will be freed, etc. It's just that: data.

And as I wrote in the other comment, don't do OOP. It's wrong. I think it has had a good twenty years of hype to prove any merits, and failed to do so. Yes, OOP without a garbage collector cannot be done reliably because the pointers will kill you. But that's not an indication that pointer management is impossible. It's an indication that we shouldn't do OOP. Even in a GCed language it's still a pain to maintain all these redundant links, and to write algorithms that collect the data that was littered in a thousand places and is accessible only through a hard to understand object interface.

There are much simpler ways to organize a program. Pointer management can be pretty easy when the project structure is right, and we focus on the data instead an overly bureaucratic object graph of funky objects that do almost nothing in the name of "reusability".

Re:You can't Rust that by klmr on Monday April 02, 2018 @09:07AM (#16734145)
It’s not that simple. First off, OOP is actually useful if not abused. Even in modern C++ it definitely has its place. And secondly, you run into the same problem outside the limited scope of OOP. Every large, well-designed library provides its own solution to this problem of “holding onto” a value and extending its lifetime, if necessary. Obviously I agree with you that a bare pointer shouldn’t ever be (ab)used for this purpose but as mentioned that’s easier said than done, since the alternatives aren’t trivial. Lifetime extension really is difficult.
Re:You can't Rust that by jstimpfle on Monday April 02, 2018 @11:10AM (#16735141)
My general approach is to divide a project into modules which hold their data in global tables (typically a handful of dynamically allocated arrays). The data's lifetime is managed by that module and that module only. Most of the data never needs to be exposed to other modules, and most of the functionality related to that data is encapsulated in the module.

Other modules use integer handles to access data. They might convert the integer handle to a pointer and iterate over the data with the help of the module, but these pointers are not stored. The module's interface has very clear and simple rules which commands from its API invalidate those pointers (because they might trigger reallocation). But of course only few commands do invalidate pointers, and they are typically not interleaved with the other commands. And typically getting pointers is not even needed, because the relevant functionality is implemented in the module that owns the data.

There are generally no pointers inside the data (perhaps a few const char pointers to static or quasi-static data) and most intra-module links are realized using integer indexes, just like it's done in relational databases.

So in a way that is actually "object-oriented", but not in the usual sense of creating a million instances of some class that are linked in a complicated way. Every module is a kind of object. So the number of these objects is typically static. All the linking between these objects is simply done by: the linker :-)