1. It really helped me to stop trying to force my code to be as pretty as possible the first time around.
2. If you are getting errors about a lifetime not living long enough in a complex expression, it can help to pull some part of it out into a helper let binding.
3. Adding an artificial scope (a curly block in the middle of another block) can be a great way to control the lifetime of a borrow.
4. clone() all the things! This is a major advantage that Rust has over other ball-and-chain languages like Haskell. When you get into a type-tetris situation in Haskell, you just have to find a way out. In Rust you can often just clone() something that you would rather not have to clone() and move on. I love being able to tell the borrow checker to go away while I concentrate on my program logic. Later I can always go back and fix it.
extern crate xpath_reader;
extern crate regex;
use std::io::prelude::*;
use std::fs::File;
use xpath_reader::Reader;
use regex::Regex;
fn main() {
let mut contents = String::new();
File::open("test.xml").expect("Unable to open the file").read_to_string(&mut contents).expect("Unable to read the file");
//println!("File contents: {}", contents);
let reader = Reader::from_str(&contents, None).expect("Cannot parse XML file");
let files: Vec<String> = reader.read("//file").expect("Cannot find file entries in XML file");
//println!("Files: {:?}", files);
for file in files.iter() {
println!("File: {}", file);
let mut contents = String::new();
File::open(file).expect("Unable to open the file").read_to_string(&mut contents).expect("Unable to read the file");
//println!("File contents: {}", contents);
let re = Regex::new(r"foo bar").expect("Cannot compile regex");
let replaced = re.replace_all(&contents, "baz").to_string();
//println!("File contents: {}", replaced);
File::create(file).expect("Unable to overwrite the file").write(replaced.as_bytes()).expect("Unable to write to file");
}
}It's not the typing that is slow - it's the constant "why can't I do it like this?" stackoverflow trips. For example - how do you open a file in read-write mode? Using File::open()? Nope!
Ok that one is just poor API design - most of the time was really in restructuring to make the borrow checker happy, and fiddling with adding and removing '&' and 'ref' to get the right types.
The fiddling with borrows isn't about being as optimised as possible, it's about being correct in the absence of garbage collection. Are you sure you need no GC and no runtime? Tasks like the script you describe sound like a perfect fit for something like OCaml.
That doesn't surprise me at all. Writing it in C++ would probably take me a lot longer than writing it in Python too.
[1] http://benchmarksgame.alioth.debian.org/u64q/compare.php?lan... [2] https://benchmarksgame.alioth.debian.org/u64q/compare.php?la...
stirner seems to mean something different by "competitive".
> System programming requires a great degree of hardware awareness. Its goal is to achieve efficient use of available resources, either because the software itself is performance critical (AAA video games) or because even small efficiency improvements directly transform into significant monetary savings for the service provider (cloud based word processors).
So the question is, having been able to achieve half of the allowed budget, why pay for more development effort?
Of course, if the bugdet is only achievable with a lower level language, there is no way around using it.
Personally, I don't think this is even an interesting metric. These are tools for experts, understanding expert experiences is more interesting.
To address the heart of what you're getting at: I don't buy the fact that it took "10x" longer to read an XML file. I'll give you some amount of "longer" just by virtue of Rust's general strictness and lengthy semantics (like any systems language), but you can't just squash together your unfamiliarity with the language or its libraries and use that to determine the base line difficulty of implementation. To highlight this, consider a seasoned C# developer trying to read an XML file in python for the first time. It would probably take them "10x" longer in python too, and it won't be because the languages have different purposes/audiences or the languages' rules and conventions.
I'd take the view that "a little script to read an XML file, get a path from it, read a file at that location, do some regexes on it, and then update the XML and write it back to disk" doesn't need any more speed than Python, but can benefit from more correctness. I'm willing to spend a bit more implementation time than Python to get more safety than Python, but I'm not willing to spend extra time just to make it faster (and frankly I'd question the priorities of someone who would). If implementing in Rust is slower than a more error-prone language, that's not necessarily a mark against it - but if I can implement faster than Rust in a language that would also have similar safety properties then I'd rather do that, even if it meant somewhat slower runtime performance.
Why? Does this really make sense? You write it once, it runs a multitude of times. Why is the extra 30 minutes (maybe) to implement in Rust worth more than the extra CPU cycles over countless runs? It's going to be less error prone and faster.
CPU cycles are cheap - my CPU is idle most of the time - and my time is expensive. Given that the script in question is fetching a file over FTP, the time taken for that is going to swamp any compute time (even in the Python implementation). Even if you could make a version that ran instantly, how many times would I have to run the script to add up to saving 30 minutes of my time? It doesn't sound like the kind of script that's going to be in use for decades.
> It's going to be less error prone and faster.
Sure, but the overwhelming majority of the time "faster" doesn't matter. So I'm much more interested in comparing languages that have the same safety properties, and whether it's possible to get the same "less error prone" effect from a language that's easier to work in than Rust.
Out of interest what setup are you using? My editor is telling me whether a variable is a reference or not so maybe this is just a plugin you don’t have? I’m currently using Rust Enhanced (with RLS), I believe VSCode shows you this information too.
I've rarely found this the case for me but I came from C++ and am generally used to thinking in terms of lifetimes. Could you elaborate on how you ran into problems with borrows messing up code structure?
> Dealing with strings is another pain point. I get why there is `&str` and `String`. But that doesn't explain why I can't add two `String`s together using +.
I feel like the Rust stdlib takes after C++ in making costs obvious to people and not providing shortcuts for slow operations, causing the ergonomics to scale with performance.
Something that some of us in the CLI-WG have been talking about is creating a library that interops with the standard library but instead focuses on python-like semantics at the cost of performance. This will be a big benefit for "quick scripts" and people learning the language while allowing people to still fall back to high performance idioms as determined by a profiler.
Unfortunately, this is a little lower on our priority list for now.
Which kind of makes me wonder if a language like the one you wished for existed would it not be better to pick one of the existing languages instead and maybe use type annotations to help in Node or Python?
Then how do you explain the constant push for "more ergonomics"?
Which has not been my experience.
Because that would consume both Strings, which doesn't make sense. The impl that exists only consumes the left string, reusing its buffer.
This seems like an odd thing to complain about since if you try it the compiler will tell you exactly what you should do instead.
I think a lot of proponents might argue that Python is better than Rust for very, very small scripts like this, but as your program grows, Rust pays its dividends quickly. This hasn't been my experience with Rust in my 4 years with it; it makes certain classes of errors harder, but I've not yet gotten to the point where the compiler is actually saving me time--the time I spend pacifying rustc is more than enough to write it in Go (or even Python) and get my extra confidence from tests. I like the idea of Rust, but it's hard for me to justify it for the applications I tend to write.
I'd estimate it took me about 10 times longer to implement in Rust than Python. And I don't exactly know Python well - it wasn't just extra time Googling how to do things.
A lot of it is just how restrictive the borrow checker is. Often you have to structure your code in a really weird way to satisfy it.
Dealing with strings is another pain point. I get why there is `&str` and `String`. But that doesn't explain why I can't add two `String`s together using +.
Another thing I've found is that because of the type inference it often gets really confusing whether a variable is a reference or not. Doesn't help that code completion basically doesn't work at all at the moment.
I wish there were a simpler language, like Rust (no garbage collector, no runtime), but that was a little more helpful and willing to do implicit things even if they are slightly slower than the most optimised code possible.