Live data from Hacker News

Flattening Rust’s learning curve

corrode.dev

371–380 of 405 posts

Re: Flattening Rust’s learning curve

#371
post #204

Earlier quoted context omitted.

But it is true. My own biggest mistake when learning Rust was that I tried to torce Object Oriented paradigms on it. That went.. poorly. As soon as I went "fuck it, I just do it like you want" things went smoothly.

Sounds like an abusive relationship if im being honest. Your programming language shouldnt constrict you in those ways.

If your compiler does not let you compile garbage code, then it's restricting you, but that's exactly what you want - you don't want to compile something that is incorrect. Rust just enforces more rules than, say, Ruby or C/C++.

Re: Flattening Rust’s learning curve

#372
post #206

Earlier quoted context omitted.

> Why can mutable reference be only handed out once? Here's a single-threaded program which would exhibit dangling pointers if Rust allowed handing out multiple references (mutable or otherwise) to data that's being mutated: let mut v = Vec::new(); v.push(42); // Address of first element: 0x6533c883fb10 println!("{:p}", &v[0]); // Put something after v on the heap // so it can't be grown in-place let v2 = v.clone();…

> // Put something after v on the heap > // so it can't be grown in-place > let v2 = v.clone(); I doubt rust guarantees that “Put something after v on the heap” behavior. The whole idea of a heap is that you give up control over where allocations happen in exchange for an easy way to allocate, free and reuse memory.

It certainly doesn't guarantee it, this is just what's needed to induce a relocation in this particular instance. But this makes Rust's ownership tracking even more important, because it would be trivial for this to "accidentally work" in something like C++, only for it to explode as soon as any future change either perturbs the heap or pushes enough items to the vec that a relocation is suddenly triggered.

Re: Flattening Rust’s learning curve

#373
post #206

Earlier quoted context omitted.

> Why can mutable reference be only handed out once? Here's a single-threaded program which would exhibit dangling pointers if Rust allowed handing out multiple references (mutable or otherwise) to data that's being mutated: let mut v = Vec::new(); v.push(42); // Address of first element: 0x6533c883fb10 println!("{:p}", &v[0]); // Put something after v on the heap // so it can't be grown in-place let v2 = v.clone();…

The analogous program in pretty much any modern language under the sun has no problem with this, in spite of multiple references being casually allowed. To have a safe reference to the cell of a vector, we need a "locative" object for that, which keeps track of v , and the offset 0 into v.

> The analogous program in pretty much any modern language under the sun has no problem with this, in spite of multiple references being casually allowed.

And then every time the underlying data moves, the program's runtime either needs to do a dynamic lookup of all pointers to that data and then iterate over all of them to point to the new location, or otherwise you need to introduce yet another layer of indirection (or even worse, you could use linked lists). Many languages exist in domains where they don't mind paying such a runtime cost, but Rust is trying to be as fast as possible while being as memory-safe as possible.

In other words, pick your poison:

1. Allow mutable data, but do not support direct interior references.

2. Allow interior references, but do not allow mutable data.

3. Allow mutable data, but only allow indirect/dynamically adjusted references.

4. Allow both mutable data and direct interior references, force the author to manually enforce memory-safety.

5. Allow both mutable data and direct interior references, use static analysis to ensure safety by only allowing references to be held when mutation cannot invalidate them.

Re: Flattening Rust’s learning curve

#374
post #92

It took me a few tries to get comfortable with Rust—its ownership model, lifetimes, and pervasive use of enums and pattern matching were daunting at first. In my initial attempt, I felt overwhelmed very early on. The second time, I was too dogmatic, reading the book line by line from the very first chapter, and eventually lost patience. By then, however, I had come to understand that Rust would help me learn programm…

Your experience matches an observation I have made, that when C++ developers approach Rust for the first time they often "fight the borrow checker" when they use C++ idioms in Rust. Then they start to learn Rust idioms, and bring them back to C++, which causes them to write more robust code despite not having and borrow checking at all.

For the most part true, but there exists patterns I can do safely and easily in c++ which I cannot in rust. Structured concurrency being one of the major ones. If a child object takes a reference to the parent, I shouldn't need to do it by way of an Arc, but because of the fact that leaking memory is safe, this isn't possible to do in rust without using the unsafe keyword. So I end up with more refcounting that I want. (This is often in the context of async). I don't bring this pattern back to c++ with me.

Re: Flattening Rust’s learning curve

#375
post #18

It's like reading "A Discipline of Programming", by Dijkstra. That morality play approach was needed back then, because nobody knew how to think about this stuff. Most explanations of ownership in Rust are far too wordy. See [1]. The core concepts are mostly there, but hidden under all the examples. - Each data object in Rust has exactly one owner. - Ownership can be transferred in ways that preserve the one-owner ru…

I still haven't gotten into rust yet, mostly due to time and demand, but, I have been doing a lot of C++ in the past few years.

Coming from that background these rules sound fantastic, theres been a lot of work put into c++ the past few years to try and make these things easier to enforce but it's still difficult to do right even with smart pointers.

Re: Flattening Rust’s learning curve

#376
post #69
post #18

It's like reading "A Discipline of Programming", by Dijkstra. That morality play approach was needed back then, because nobody knew how to think about this stuff. Most explanations of ownership in Rust are far too wordy. See [1]. The core concepts are mostly there, but hidden under all the examples. - Each data object in Rust has exactly one owner. - Ownership can be transferred in ways that preserve the one-owner ru…

The second bullet in the second section is overpromising badly. In fact there are many, many, many ways to write verifiably correct code that leaves no dangling pointers yet won't compile with rustc. Frankly most of the complexity you're complaining about stems from attempts to specify exactly what magic the borrow checker can prove correct and which incantations it can't.

Rust feels like an excellent language paired with a beta-quality borrow checker. The issue is that the more they fix the paper cuts, the more complex the type system grows.

Re: Flattening Rust’s learning curve

#377

Rust is wonderful but humbling! It has a built in coach: the borrow checker! Borrow checker wouldn't get off my damn case - errors after errors - so I gave in. I allowed it to teach me - compile error by compile error - the proper way to do a threadsafe shared-memory ringbuffer. I was convinced I knew. I didn't. C and C++ lack ownership semantics so their compilers can't coach you. Everyone should learn Rust. You nev…

> Borrow checker wouldn't get off my damn case - errors after errors - so I gave in. I allowed it to teach me

Bondage driven development.

Re: Flattening Rust’s learning curve

#378
post #376
post #69

Earlier quoted context omitted.

The second bullet in the second section is overpromising badly. In fact there are many, many, many ways to write verifiably correct code that leaves no dangling pointers yet won't compile with rustc. Frankly most of the complexity you're complaining about stems from attempts to specify exactly what magic the borrow checker can prove correct and which incantations it can't.

Rust feels like an excellent language paired with a beta-quality borrow checker. The issue is that the more they fix the paper cuts, the more complex the type system grows.

What do you mean? What are some examples of borrow checker improvements that resulted in more type system complexity?

Re: Flattening Rust’s learning curve

#379

Earlier quoted context omitted.

I think your comment has received excellent replies. However, no one has tackled your actual question so far: > _who_ is the owner. Is it a stack frame? I don’t think that it’s helpful to call a stack frame the owner in the sense of the borrow checker. If the owner was the stack frame, then why would it have to borrow objects to itself? The fact that the following code doesn’t compile seems to support that: fn main()…

I believe this answer is correct. Ownership exists at the language level, not the machine level. Thinking of a part of the stack or a piece of memory as owning something isn’t correct. A language entity, like a variable, is what owns another object in rust. When that object goes at a scope, its resources are released, including all the things it owns.

> Ownership exists at the language level, not the machine level.

Right. That's the key here. "Move semantics" can let you move something from the stack to the heap, or the heap to the stack, provided that a lot of fussy rules are enforced. It's quite common to do this. You might create a struct on the stack, then push it onto a vector, to be appended at the end. Works fine. The data had to be copied, and the language took care of that. It also took care of preventing you from doing that if the struct isn't safely move copyable.

C++ now has "move semantics", but for legacy reasons, enforcement is not strict enough to prevent moves which should not be allowed.

Re: Flattening Rust’s learning curve

#380
post #288

Earlier quoted context omitted.

Cloning doesn’t imply heap allocation. Depends on the type.

If the object had a stack-bounded lifetime, the borrow checker would have been able to prove the analysis though. The advice is to clone things it can't, which pretty much requires that it go into the general heap. I'm sure there are some interesting counterexamples, but the situation you're imagining seems kinda academic.

The fact something goes to heap doesn’t necessarily mean it needs more heap allocations. I’ve had it many times that I instantiated a new object on heap and had to pass cloned arguments to it (because it had to own them), yet they ended up as inline fields, so no additional allocations. Happens a lot with Rc / Arc.
Post reply on HN