9 ms·
Here I am on day three of an attempt to modify a rust program with logic that would have taken about twenty minutes to implement in C# or Java. It has to do wit
by ahelwer 6y ago
Here I am on day three of an attempt to modify a rust program with logic that would have taken about twenty minutes to implement in C# or Java. It has to do with operations on strings (use a regex to find a string in some text, escape all the regex-reserved characters in that string, then use the string as a regex to find other occurrences of itself in the text) so I get that I'm really thrown into the borrow-checker deep-end, but man writing rust feels like working on one of those puzzles where you try to fit a set of tiles inside a rectangle. You'll almost get it but then some edge is sticking out. So you move the tiles around but this leads to two edges sticking out now! So you do a whole bunch of additional exploring before ending up right back where you started with the one edge sticking out.
I even abandoned the learn-by-stackoverflow-search approach to rust and read the first seven or so chapters of the rust book. But even that hasn't helped very much. I know I am just currently in the painful, frustrating stage of learning where nothing really makes sense, and at some future point it will all click, but it really can't be overstated just what a wicked learning curve this language has.
- ajross 6y agoThat's where I always end up when I start playing with Rust too. At this point I genuinely think I've given up on belief that the "borrow checker" paradigm is ever really going to be broadly successful (vs. "screw it, just do it in <other managed language>"). Problems with well-constrained allocation paradigms do really well in rust. Problems with reading/parsing/storing messy data structures from external data just really, really hurt. I think a big part of the problem is that the ownership analysis layer has the dual crises of being (1) really complicated and too hard to keep in your head as a single model and (2) specified in an ad-hoc way that resists formal rules. So eventually you end up in of those puzzles like you mention where... there's just no answer! No one's been where you have, and the lack of rigor means you end up trying to reverse engineer the compiler front end trying to find the trick that works. And then you give up and write it in Go or Python or whatever.
- sanxiyn 6y agoNo, borrow checker's formal rules are rather simple. Here it is: It is an error to access a place, when an access conflicts with a loan, and the loan is live.
- ajross 6y agoYou're describing the desired behavior of the compiler, but not what it actually implements. In fact there are an infinite number of correct Rust programs which will never access memory incorrectly but which will still be rejected by the compiler, for the simple reason that Rust's authors, talented though they may be, have made no progress at all at solving the Halting Problem. What Rust actually accepts is a subset of correct programs. And this subset is somewhat informally defined as "whatever the compiler could manage to prove". Real code hits against this limit occasionally, and when it does there's really no option other than "join the Rust team" or "try voodoo".
- sanxiyn 6y agoNo, the rule I stated is what is actually implemented. Like all type checking technology, the rule is about static behavior, not dynamic behavior. So halting problem is irrelevant. Rust accepts all programs that follow the rule in static behavior. It's not a subset, and it's not a best effort to prove. It's the complete statement. "Access" means source code construct to read or write. Rust doesn't care whether the construct is actually executed in runtime.
- estebank 6y agoI believe the "subset of correct code" refers to things like the current non-support for things like let (left, right) = (&mut foo[..split], &mut foo[split..]); where you have to rely on things like foo.split_at_mut(split), which are implemented as unsafe under the cover. I see these are limitations but not show-stoppers in any way.
- sanxiyn 6y agoYes, but the rule is still complete. What is going on is "place" is currently defined to be local x, or field x.f, or index x[i], but all x[i] are merged to x[*]. Rust's so-called NLL changed definition of "live". The point is, the rule is simple, formal, and complete. It is neither an ad hoc rule nor a best effort prover.
- CJefferson 6y agoWhile this can be painful, my basic suggestion -- clone more. The temptation is to never clone, butin C++ people run copy constructors all the time without thinking about it (as they are called automatically).
- ocschwar 6y agoCloning means runtime inefficiency, but the cost is so pathetically small compared to the amount of code we run in Python and Java with their larger footprints. Yes. Clone more.
- xena 6y agoDoesn't the compiler optimize out clones when it can?
- sanxiyn 6y agoNo, not really.
- tylerhou 6y agoProbably not; detecting unnecessary clones is expensive for non-trivial cases. If a human can't prove that the clone isn't necessary (by writing the code in such a manner that the borrow checker accepts), I doubt the compiler could.
- toby 6y agoThere's a Cargo tool called "clippy" which will tell you if it finds unnecessary clones.
- rogers18445 6y agoThe vast majority of the code you write is glue code, you can be as inefficient when writing it as you want and it doesn't matter. Optimize only what the profiler tells you your cpu is spending real time in.
- brundolf 6y ago
- hardwaregeek 6y agoI don't know if this helpful, but from your description of your problem, here's how I'd do that in Rust: - Use a regex to find a string in some text: use regexes, get a &str - Escape all the regex reserved characters in that string: Okay this requires mutation but we can't mutate a &str, let's loop over the &str's characters (using the Chars iterator) and push them into a new String with the proper escapes - The new String we own, so we can easily turn it into a regex and use it to search the original string.
- nemo44x 6y agoYes, lots of up front things that pay off after your code base gets larger and/or you ship.
- deleted 6y ago[deleted]
- sanxiyn 6y agoYou can't learn Rust by StackOverflow, that just doesn't work. It is well known, I repeat this everytime I see it happening, and they never listen. It's deeply frustrating.
- ahelwer 6y agoI mean yeah, that's why I went and read a bunch of the book. But I still run into lots of issues. Like what the hell is a borrow cow and when would I want to use it? And how do I fix the "temporary value dropped when borrowed" error in a series of maps on options?
- sanxiyn 6y agoClone more.
- ahelwer 6y agoSee that's seemingly good (and popular) advice, which I didn't find anywhere in the book.
- steveklabnik 6y agoIt's in chapter 12 https://doc.rust-lang.org/stable/book/ch12-03-improving-error-handling-and-modularity.html#the-trade-offs-of-using-clone https://doc.rust-lang.org/stable/book/ch12-03-improving-erro...
- estebank 6y agoIf you don't mind sharing snippets for the diagnostics you couldn't figure out, I would love to see them in case we can mechanically suggest the appropriate code, but in general a well placed .clone(), .cloned() or .collect() will be what you want. Likely what's happening is you're iterating over a sequence (you can think of options as a sequence of only one iteration as well), but that iterator is over borrows of the sequence's data, instead of owned values. If you try to return that outside of its enclosing function, you will see that error. As for a "borrow cow", you probably want to read https://doc.rust-lang.org/std/borrow/enum.Cow.html https://doc.rust-lang.org/std/borrow/enum.Cow.html and https://deterministic.space/secret-life-of-cows.html https://deterministic.space/secret-life-of-cows.html. For what you are doing, it is an optimization to avoid unnecessary clones that is absolutely not required: make your code work with String and worry about Cow<'_, str> later.
- modeless 6y agoI had a similar experience. It really made me question whether the borrow checker is worth it. I'm all for memory safety, but there are some pretty compelling low pause GCs out there now that can give you memory safety without the ergonomic problems.
- devit 6y agoThat seems rather trivial, and I'm not sure how you could possibly have any issues with the borrow checker. Just do the first regex search, then escape into a String with regex::escape, then build a regex from the String and search with it. Of course what you are doing is probably wrong since you should use a string matching crate rather than escaping characters and using a regex matching crate.
- ahelwer 6y agoSince it's so trivial, I would be quite happy for you to implement the feature for me! https://github.com/tree-sitter/tree-sitter/issues/982 https://github.com/tree-sitter/tree-sitter/issues/982 Just lol at the idea that you won't run into borrow checker issues when dealing with strings though.
- burntsushi 6y agoSolving that issue isn't trivial. I just read it and I wouldn't know where to begin, probably because I don't understand the requirements. I think what's being called "trivial" is doing a bit of regex searching. It's probably accurate to call that trivial for an experienced Rust programmer, but if you're just beginning, I don't think it's helpful to call anything trivial. I still remember my first exposure to Rust. It was different. It took a bit to grok. But once it clicked, things were much better. As the maintainer of the regex crate, I invite you or anyone to ask for help using regexes. The regex repo has Discussions opened up, so it's appropriate to ask for help, even if they are beginner questions: https://github.com/rust-lang/regex/discussions https://github.com/rust-lang/regex/discussions As usual though, try to provide as many details as you can. Giving the source code you have but can't get to work is a great start, for example.
- estebank 6y agoLooking at their current iteration of the code, the problem is not in the regex crate's API, but rather on them not having the ownership system internalized yet. https://doc.rust-lang.org/rust-by-example/ https://doc.rust-lang.org/rust-by-example/ and https://github.com/rust-lang/rustlings https://github.com/rust-lang/rustlings might be good places to visit to gain that experience, as well as improving the specific diagnostics to be more actionable.
- iudqnolq 6y agoI'm in a similar position. I highly recommend posting snippets of what you're trying to do in the Rust discord. The people are super nice and very helpful. Honestly the friendliness of the community is a big part of why I'm learning Rust. I found it so much harder to get free help from volunteers with c code issues. Not that I deserve it or anything, but it's really nice and I try to contribute back by writing PRs to improve the docs once I understand something.
- volta83 6y ago> use a regex to find a string in some text, escape all the regex-reserved characters in that string, then use the string as a regex to find other occurrences of itself in the text) This took 2 min: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=38f413f1ba9d12bae6fc85d27e24fc0e https://play.rust-lang.org/?version=stable&mode=debug&editio... and I don't recall what's the last time I used the regex crate. I'm certainly doing all the regex stuff incorrectly, no idea how to match regex-reserved characters, no idea if I'm using the iterator API correctly (its the second example in the regex docs..), etc. But the idea is simple, search the text for the regex, escape all the reserved characters, use that regex to search for occurrences of the same regex. I guess that if you want a fully-vectorized, zero-allocation, zero-copy, multi-threaded, gpu... version of this that maxes out the theoretical peak of the hardware available, then you'll probably end up putting a considerable amount of work here. But otherwise, for someone with literally 30 seconds of experience with the regex crate, it was as straightforward to implement as you mentioned. Just c&p their second example, and just added some glue. This is how I'd implement this in Python, or Java... EDIT: other responses suggest cloning, but that seems to suggest that one has to explicitly and deliberately think about cloning. I instead just played a game of type golf using functions like `.to_string()` or `.collect` to "clone" stuff behind the scenes. My code is probably horrible, but is the first thing that came to mind.
- puetzk 6y agoAlso, this function is just regex::escape https://docs.rs/regex/1.4.5/regex/fn.escape.html https://docs.rs/regex/1.4.5/regex/fn.escape.html
- brundolf 6y agoShort suggestion: If performance isn't critical, sprinkle some .clone()s in there on your strings and the borrow-checker will get off your back Long suggestion: Working with strings in Rust requires that you have a C++ mental model of what strings are and what properties (in the abstract sense of the word) they have. Rust will make sure you can't misuse them, and it will make them as ergonomic as they can be within that C++-like model, but it's a fundamentally different concept from what you have in languages like C# and Java, and if you're trying to look at things from that other perspective, you're going to keep running into walls over and over. More broadly, when you're using Rust you need to be in the basic mindset of a C++ programmer. From there Rust will make things significantly easier in many ways, but if you don't start with the right mental model, you're going to have a bad time. Strings are probably the most striking case of this because the way they get treated in virtually all higher-level languages is so wildly different from the way they get treated at the low-level.
- Jweb_Guru 6y agoAs someone who's been there, I strongly recommend asking for help (maybe posting this on HN counts?). Bashing your head against the wall against Rust without some correct code to look at is not (IMO) a great way to learn the language... I definitely get the appeal, as someone who hates asking for help, but it's just not worth it in this case, especially when you're starting out and aren't even sure what terms to look for.
- cbHXBY1D 6y agoThis is my experience with the language. It's a good language - but it's a tool at the end of the day. One with edges and trade offs like any other. I can't help but think the people like the author only feel "giddy" because its been hyped up on this website.