6 ms·
I have no idea what this means, and I have a masters in Computer Science and 20 years of industry experience. Enlighten me?
by akie 10y ago
I have no idea what this means, and I have a masters in Computer Science and 20 years of industry experience. Enlighten me?
- tatterdemalion 10y agoThis is not a great way to ask a question. Your degrees and years of experience are not relevant to understanding the subject matter of this post. In order to enlighten you, we'd need to know what specifically was confusing about this article to you. What did you not understand?
- HerraBRE 10y agoI think it was actually a nice nod to all the folks who haven't got that much experience, and maybe felt silly for not understanding the code snippet. :)
- rubber_duck 10y agoIn C++ it's quite easy to get burned by keeping a reference to something where the owner went out of scope eg. class Foo { int bar; int& getBar() { return bar; } } if you take ref of bar and Foo goes out of scope well fuck. It gets even worse, eg. : vector<int> foo; int& bar = foo[i]; foo.push_back(1) that last line can cause foo to reallocate the buffer and bar is then invalid, meanwhile that code will compile without warnings (well I'm guessing it will I haven't done C++ in a year :) )
- akie 10y agoThank you! That clarifies it quite a bit. My C/C++ is too rusty to remember these internals...
- saghm 10y ago> My C/C++ is too rusty Pun intended?
- satbyy 10y agoJust hit 'Run' here: http://rextester.com/BDGO47890 http://rextester.com/BDGO47890 You can see the program crash
- rubber_duck 10y agoNice, but the example crashes because you take a reference to uninitialized vector, if you initialize it and grow the vector I found that it will either keep "working fine" if you don't expect the data to change or randomly give garbage data - depending on compiler/stdlib - especially since the time when the buffer grows is implementation defined. Here is a better example : http://rextester.com/MAJDQ8375 http://rextester.com/MAJDQ8375 On VC++ it will return : Reference is 0 but value is 9 On clang it will return : Reference is 32579632 but value is 9 So let's say I wasn't mutating the 0 index, VC example would work fine at runtime and then clang would suddenly start giving garbage results. This has caused me week of debugging in a big code base.
- satbyy 10y agoThanks for the catch!
- orf 10y agoI'd hope someone with that much experience would be able to grok at least some of it? I guess it isn't explained that well though. As I understand it, if you have a structure that has a pointer to something (like a User struct pointing to an array of permissions or some such), and the memory pointed to will be freed when the structure is freed. There is a case where code can keep a reference to this pointer around longer than the user struct it belongs to, so when code tries to use it it references an invalid pointer. This is bad. Rust takes care of this in some clever ways, so you cant have a pointer/reference that outlives it's parent.
- tatterdemalion 10y agoYou've missed a critical point in the article: the structure does not have a pointer to something it has a member inline within its stack representation. Any garbage collector could (with some overhead) prevent invalid memory access when we're talking about an object with a pointer to another object. Rust is able to validate the correctness of pointers to offsets within the stack representation of an object. (Rust also validates the correctness of references to heap allocated objects as well, of course!)
- m1el 10y agoLet's translate this code to C++: class X { public: Y* getY(void) { return &y; } private: Y y; }; } `getY` returns a pointer to the struct field, this field is allocated as a part of the struct. There is no way (in C++) to verify that this pointer does not outlive the struct itself. In other words, your pointer may become stale, and the compiler has no way to check for it. Rust has a resource ownership model (borrow checker) that allows it to check at compile time that references do not outlive the data.
- akie 10y agoAh, I see! Thank you.
- humanrebar 10y agoA little closer would be to use a shared_ptr<X> and shared_ptr<Y> that share the same reference count (1). It's not exactly the same thing, though, since Rust gives you some safe usage checks that you don't get with shared_ptr. That is, vanilla Rust objects have lifetime semantics closer to unique_ptr than shared_ptr. (1): See constructor number 7 here, which was designed for this purpose: http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_pt...
- dbaupp 10y agoIt also isn't the same in that it comes with the cost of allocation and reference counting.
- nallerooth 10y agoThanks!
- blub 10y agoBoth the original example and your translation exhibit poor API design: it's an antipattern to return mutable references to member variables. This would remove all encapsulation, which is probably the most important principle of OOP. Nevertheless, a better solution was pointed out in an answer on the blog and by humanrebar.
- usrusr 10y agoIt's about safely exposing innards. Are you are familiar with the substring problem in Java? Before 1.7.0_06, substrings where created using an offset into the buffer of the original string. Nice O(1) substring creation, the textbook example for benefits of immutability. Unfortunately, if you have long-lived, but short substrings taken out of short-lived, but long parent strings, the garbage collector won't find much to collect, since the substrings keep references to their parent buffers. This was no showstopper, but without immutability or in an environment with unmanaged memory it might very well be. This Rust snippet is about similar cases of subcomponent sharing, just on a shorter timescale: the subcomponent is exposed in a way that allows the compiler to enforce that nobody keeps a reference for longer than it is guaranteed to be valid. You may look, you may even touch, but if you want to look again you have to ask again. Other languages get by without this by garbage collection, by excessive precautionary copying or by simply hoping that the caller won't do anything stupid. Rust is unique in achieving the same runtime behavior as the latter but without the uncertainty. As a bonus, those un-keepable references cannot escape across concurrency borders so that in-place mutability becomes a lot more manageable.