1. .clone() / .to_owned()
1. String -> vs &String/&str with & borrow
1. lazy_static / OnceCell + Lazy / OnceLock
1. Arc<Mutex<T>> with lots of .lock(). What would start off as like NULL in Java/C is Arc<Mutex<Option<T>>> and you have to check if it is .is_none()
I think that's about it. I rarely introduce a lifetime to a struct/impl. I try to avoid it honestly (probably for worse). Arc kind of bails you out of a lot (whether that's for good or not I don't know).
edit: Remembered another. I think it's kind of weird/too verbose from the compiler / borrow checker when you have a primitive like u32 and it's borrowed and you had to dereference it or clone it.
This is the right way to learn. I'm quite familiar with lifetimes and whatnot, but when I didn't bother with them much at all when learning - I just "clone"'d.
This allowed me to learn 95% of the language, and then once I did that, learn the last 5% (lifetimes).
Highly recommend.
2.) Be careful because &String is not &str, although in many cases you can pretend thanks to magic of AsRef/AsDeref.
4.) If you find yourself calling is_none, rethink things a bit. Pattern matching and the various destructuring (e.g. if let) features are some of the most powerful tools at your disposal with Rust. This is where experience with something else first (e.g. Elixir) can be helpful.
I rarely introduce a lifetime to a struct/impl.
IMO the big use case here is &str. Arc kind of bails you out of a lot
While that's totally reasonable it's good to remember that you're essentially trading compile time guarantees for runtime ones. If you can figure out how to beat the borrow checker into submission that's one less thing to debug at runtime. primitive like u32 and it's borrowed and you had to dereference it or clone it.
The primitive types should all implement Copy which means you should (almost?) never have to explicitly clone them. Dereferencing is another story tho.Nah, go with an ML-family language. Maybe even Standard ML, because it will nudge you away from writing "C in ML" and encourage you to pick up the idiomatic way of doing things. (Laurence Paulson's book has an online version available for free on his homepage).
Non-lexical lifetimes are, in my experience, pretty uncommon in most non-library code. You don't really need them until you really need them.
To avoid confusing the newcomers: lifetimes are always non-lexical (see [1] for the pedantic details.) I suppose you meant that explicit lifetime annotations are pretty uncommon, which is not wrong.
[1] https://blog.rust-lang.org/2022/08/05/nll-by-default.html
I'm not saying that's a good thing.
If you're creating a new string, then sure return String. But if you have a path where you could just return the original input, consider returning a Cow<str>.
Other things I would add to this list:
- For structs that don't own anything on the heap, derive Copy. Then you can just pass them around willy-nilly without explicit clone()s
- Using a functional style where it makes sense to helps a lot; it can be really easy to pass things by reference when they only need to be temporarily, immutably used by that one function. And if you make Copyable structs, you can pass owned structs and return owned structs and not worry about any of it
And it's even okay beyond just starting with.
Search the regex crate repository for clones. There are a lot of them. Hell, Regex::new accepts a &str for a pattern, and one of the first things it does is convert it to a String.
Rc will be faster (if that matters to you) than Arc but it can't cross threads. (Safe) Rust will check you didn't get this wrong, so there's no danger but obviously knowing ahead of time avoids writing an Rc that you then need to be an Arc instead.
Sometimes it's tidier to write the borrow in the type of a pattern match e.g. if let Some(&foo) = ... Means you won't need to dereference foo inside the block.
Have you really compared? I have. Rust was faster for "small input", but quickly got beaten by Java and other languages I tried because the cost of doing things this way grows exponentially. I suggest you run benchmarks before you make your mind up and start throwing opinions around.
Another wart is that there are some written and non-written standards on CLI arguments (e.g. long option names should start with double hyphens) that 99% of Java CLI apps violate for some reason. Maybe I'm a perfectionist but it makes me uncomfortable to use Java CLI apps.
- Enable rust-analyzer inlay hints for elided lifetimes, reborrows, etc
- Enable the `elided_lifetimes_in_paths` lint
Together, these should ensure that all lifetimes in your code are clearly visible on the screen.
1. When A is a subtype of B, it means A can be used as B (a Teacher can be used by any function that accepts a Human).
2. static lifetime lives longer any other lifetimes, so it can be used as other lifetimes.
This is my beginner-level experience with Rust. It’s amazing that the compiler can be so specific about what’s wrong. But taking the error and getting explanations that even I can understand has been tricky.
Asking different ways usually leads me to understanding it well.
> because to unify them at this point would be a breaking change
Couldn't they change this in a future edition without breaking older editions?
In short, yes, but be very wary.
Because you can pass closures into functions across crate boundaries, which requires consistent lifetime semantics, I find it unlikely that this will be implemented.
A more problematic thing is that `cargo fix --edition` should be able to transform existing code to the same meaning in a new edition, so there should be a way to opt-in for the old behavior.
However, if you don't specify the argument type, it compiles fine. It's rare that you need to specify argument or return types on a closure, so it's not actually a large issue.
fn requires_static(_: &'static str) {}
let long_lifetime: &'static str = "";
let closure = |_input: &str| -> &str { long_lifetime };
let short_lifetime = &String::new();
requires_static(closure(short_lifetime));
This code compiles currently as the returned `&str` is inferred to be `&'static str` because this is what it actually returns, but with #10 fixed it will not because lifetime elision rules say that the lifetime of the output is the same as the lifetime of the input.Common Rust Lifetime Misconceptions - https://news.ycombinator.com/item?id=23279731 - May 2020 (43 comments)
To elaborate, stating "Foo is bar" as a misconception to be clarified, and then following it up with "Baz is quux", makes it very hard to follow and clearly identify what bits of information should be ingrained. In my opinion, information should only be conveyed "in the affirmative".
For example, don't write "Foo is bar is not true", write "Foo is NOT bar". Or have some consistent and unmistakable typography for the "false statements" (highlighting or color, etc)
THIS ^^
I stopped reading after about paragraph two, when I realized that it was very unclear that the code and table that I was reading was in the "this is a false belief" part of the explanation.
Don't do that.
Communicate more clearly and the article will be useful to more people, longer.