3 ms·
I've been keeping an eye on crates.io stats - it's definitely booming. ~50 new crates a day (some of them are total nonsense/tutorial work/etc.). I keep wonder
by BlahGod420 6y ago
I've been keeping an eye on crates.io stats - it's definitely booming. ~50 new crates a day (some of them are total nonsense/tutorial work/etc.).
I keep wondering about whether the hype is justified - I must be slowing down because I find myself hanging for long periods of time on borrow checker errors. One of the errors has stopped my progress dead for a week now, I swear it worked a week ago and then Rust decided that a borrow I was doing was no good.
I've also heard the borrow-checker is a common hold up for new Rust developers, so I keep slogging on.
- staticassertion 6y agoA week? Might make sense to refactor, or just clone the data? Or ask the subreddit, discord, or whatever place you want for help. I haven't run into a borrowchecker issue that took me more than a few seconds in a few years. The trick is I'm not crazy about avoiding cloning - Rust is still super fast. The curse of Rust making cloning explicit, and move by default being cheap/ memcopy, is that you often feel like "if only I could express it" and that problem seems to get worse the better you are, and then for me I hit a point where I stopped caring and just got shit done. It's easy to remove clones later in my experience, once benchmarks show it matters.
- estebank 6y agoRust has one curse (it has many, but this one is critical): inefficient code is generally visible. Experienced developers hate to notice that their code is inefficient. They will balk at seeing `Arc<RefCell<T>>`, but won't bat an eye at using Python. I know because I have the same instinct! This makes it much harder to learn Rust for experienced devs because they go from the "simple code that will work but is slightly inefficient" and in an effort to improve it they land squarely in parts of the language they haven't yet developed a mental model for.
- zozbot234 6y agoThe most important mental model to develop is a model of what can and cannot be feasibly optimized. Sometimes there really is no alternative to stuff like Rc<RefCell<>>, Arc<RwLock<>> or Arc<Mutex<>> - because you'll be working with pieces of data that have to be mutated from multiple places, and don't even have a well-defined lifetime so have to be reference counted. In that case, you should write a comment to that effect and move on. It's still good to see that RefCell<> and the like, because shared mutability is known to be a source of logical bugs. It's a bit like a low-key `unsafe`.
- BlahGod420 6y agoYes, a week (no shame, damnit). The case was that I couldn't understand what the problem even was. I understood that there can be only one owner, but I didn't see how what I was doing was a problem. I literally looked at every error message it gave me and was like "so what?" I think Rust initially suggested I implement the Copy trait or something, and so I had to brush up on traits and all that (which I did), only to find that my types can't have traits because vec<T> doesn't implement the Copy trait. So I abandoned that. The problem turned out to be that I needed a minor refactoring. I think the issue eventually turned out to be that I was mutably iterating through a list reading from TCPStreams, and then trying to write back to those streams. So it was like a read_object_from_stream(stream) on each element in a vector, and then the next line was a broadcast_to_all_streams(message) (I'm psuedocoding). The fix for me (when I finally came to understand what was happening) was to read_object_from_stream(stream) in to an intermediary vector (I named it cache), and THEN iterate through the intermediary and broadcast each item. In my notes I paraphrased it as "Instead of doing a readwrite, I separated my algorithm in to a READ and then a separate WRITE", because the way things were written I was double borrowing.
- coldpie 6y agoI'm also relatively new to Rust, just about to ship my first ever project written with it. Borrow checker errors are definitely tricky. In my experience, it usually indicates that you've done something "the wrong way," which means you need to re-think it and architect it somehow else. There's often not a quick fix (unless you just made a typo or something, of course). For example, think about who owns the data in question, why it's trying to be stored in two places at once, and how you can re-arrange your data structures or code flow to avoid that condition. It gets harder to understand once lifetimes get involved, I still have to dig into the book to understand the ins & outs of that, and its syntax. Once you get the hang of writing code in a Rust-friendly manner, borrow checker errors mostly just don't happen, because you wrote your code "the right way" to begin with. I hope I described that well. It's a fascinating language, and now that I'm on the far side of the learning curve, I find it very pleasant to use. I urge you not to give up. (Have you read through and written, built, and played with the examples in the book? I found it extremely helpful to follow the exercises, and intentionally break them, to test my understanding. https://doc.rust-lang.org/book/ https://doc.rust-lang.org/book/ )
- BlahGod420 6y agoYou're absolutely right, I think your explanation is well written. I did get past the error that stopped me for a week, and yeah it is a case of tackling the same workload but in a different way (I guess 'the right way'). What surprised me is that the solution came to me while I wasn't looking at the compiler or source or anything, but was more of an "ah ha!" moment while walking around. This indicates to me that maybe I will eventually come to understand the borrow checker. Also that book is fantastic, I find myself regularly reading/re-reading it as well as "Rust By Example". Also the std docs are great to work through when I am trying to figure out a way to accomplish something.
- _bxg1 6y ago1) Use rust-analyzer if you aren't already. It decreases the time it takes to see compiler/borrow error results by about 10x. This helps enormously when you're banging your head against something, taking guesses to see what might work. 2) It's often better to slow down and think through what's going on. Make sure you have a good understanding of pointers, allocation, ownership, etc, and if you've been fighting with something for more than 10 minutes see if you can actually understand why it's upset. Rust makes it easier not to accidentally mess these things up, but you will still be chronically frustrated if you don't have a good grasp on them. 3) I've found that the more I use it the more I get an intuition for what fixes certain errors. I don't feel great about fixing something without fully understanding it, but for certain common cases it can really help with the problem of iteration speed.
- daenz 6y agoDon't get too hung up on fancy structs containing borrowed data with complicated lifetimes. Zero-copy operations via references are an optimization that you can often forego unless you need really high performance code.
- JoshTriplett 6y agoI remember it taking me a solid month or two (of side-project experimentation, not daily work) before I felt like I'd really internalized the right mental model for ownership and borrowing.
- BlahGod420 6y agoI'm about a month in. I decided to write a chat server/client to learn Rust by. Multiple side-projects sound like it would also be advantageous. Thanks for your comment.
- steveklabnik 6y agoPlease don’t hesitate to ask for help! The forums and discord are there as a resource.
- incompatible 6y agoGenerally a language with automatic garbage collection is easier to work with. Rust can still be useful in areas like the kernel.
- estebank 6y agoCould you share what the problem you're having is? People in https://users.rust-lang.org/ https://users.rust-lang.org/ and https://reddit.com/r/rust https://reddit.com/r/rust are more than happy to help, and if I could peek at a repro case running in https://play.rust-lang.org/ https://play.rust-lang.org/ I could try to help you too (and potentially fix the diagnostic in the compiler). Also, depending on what the problem is, you might have an easier to understand error in newer versions of the compiler, which is why I recommend newcomers to use nightly and update often: not because they'll use advanced nightly-only features but because we merge diagnostics improvements aimed at newcomers every week. Also, don't get too discouraged. If you can avoid doing things that require advanced lifetime checking then you can gain enough experience with the rest of the language letting you focus only on that part of the language at a later time. Also, I can confidently tell you that at some point it clicks and you start anticipating things that can cause issues.
- BlahGod420 6y agoThank you for your response. I did figure out the problem after I took the time to slowly and methodically understand what the compiler messages were saying - that being said I was looking for places to ask for help if another week went by and I was still stuck so your resources are in my favorites list now. As I wrote in another post, I came up with the solution while out walking around and it was like an "Ah Ha!" moment, I think things are finally starting to click a bit more. I purposefully chose to write a chat server/client because it lets me work through language features one at a time. There is some basic printing/reading of console input (though I did go full blown Cursive), some networking, threading, and lots of string manipulations. I am not sure when lifetimes will come in to play, but I have started reading about them and I try to think about the lifetimes of my objects when I write.
- nilkn 6y agoOut of the box, Rust encourages almost everything to be stack allocated, imposes a read-write lock on every value (i.e., you can have multiple immutable readers, but only one mutable writer at a time), and uses move semantics for variable assignments (i.e., values aren't copied but rather ownership of the value changes from one variable to some other variable). Sometimes you need to drop one or several of these properties. It's very easy to do that -- Rust just makes you be explicit about it. If you want something to be heap allocated, you can use a Box/Rc/etc. If you want a value shared among multiple writers, there are several options; a common one is Arc<Mutex<T>>, which is also thread-safe. If you don't want move semantics, you can just copy the value using .clone(). A few lessons to take away, though: * Because of that read/write lock I mentioned, it follows that you generally want to avoid taking a mutable reference to an entire data structure, because doing so effectively puts that writer lock on the whole thing. If you can take a mutable reference only to the piece you actually care about, that can help out a lot in some cases. * Rust makes you feel like you should never clone, but really it's up to you. Exercise basic judgment that you'd use in any other language, and don't let perfect be the enemy of the good. In many cases it's probably not nearly as expensive as you might feel it is, and sometimes it'll just be optimized away in the end anyway. * Try to stick with structs and data structures that own their data as your default, deviating when it makes sense. I've seen people try to avoid ever cloning values by having many structures only contain references, which leads into complicated lifetime management that they weren't prepared for (and that may not even be possible or make sense). * Take a moment to understand the various wrapper types (I like [0]). Intuitively, Rust gives you a set of guarantees that you can ask for, and you just pick the guarantees you need and compose them together. [0] https://manishearth.github.io/blog/2015/05/27/wrapper-types-in-rust-choosing-your-guarantees/ https://manishearth.github.io/blog/2015/05/27/wrapper-types-...