7 ms·
Cool, but doesn't really make Rust less intimidating for novices I'm afraid. I mean, it's been some time since I last saw a language that needed a cheat-sheet e
by rob74 6y ago
Cool, but doesn't really make Rust less intimidating for novices I'm afraid. I mean, it's been some time since I last saw a language that needed a cheat-sheet entry for concatenating strings:
format!("{}{}", x, y)
- gourlaysama 6y agoTo be fair, this is more a Rust Syntax Reference Sheet than any kind of cheat-sheet. It basically has everything. It's useful to quickly lookup the syntax for something you never use, for example. Or to browse and discover things you didn't know were even possible.
- yakubin 6y agoA simple "+" works as well, as long as "x" is a String, and not a &str. Also, concatenating strings for any other purpose than printing them is a rarely done thing in my experience, no matter the language. So I don't see why we should optimize the ergonomics of something like that.
- the_mitsuhiko 6y agoString concatenation is an antipattern in general. Making it too convenient is not a great idea anyways.
- moldavi 6y agoWhy would concatenating strings be an antipattern?
- burntsushi 6y agoBecause it's very easy for it to become accidentally quadratic in a loop.
- talideon 6y agoThere are lots of reasons it can be. Here's an example: in many languages, concatenation requires reallocation to generate a new string object. This is especially the case where strings are immutable. That can get expensive quickly as you're producing lots of garbage for little benefit, and each concatenation requires progressively larger and larger reallocations. You end up seeing quadratic runtime costs in terms of both memory and speed. There are a few different approaches taken to this. In Python, the popular option is to append everything to a list, and use `''.join(lst)` at the end to concatenate everything. Java and C#, on the other hand, have StringBuffer/StringBuilder types to accomplish something similar. Generally, you want to avoid simple string concatenation in loops because of this, and it's not always obvious when string concatenation is being done in a loop, such as where the string concatenation is being done in a method that's being called by some other code.
- kevincox 6y agoString concatenation isn't expensive in Rust. Since the + operator consumes the first argument it will reuse the buffer and allocate quadratically. The only possible performance downside is that it can't count all of the lengths in advance and do a single allocation.
- estebank 6y agoAdd<&str> for String in a loop isn't as problematic as adding two, for example, Java Strings because of the capacity growth curve and buffer reuse, but it is still worse than not having a `String::with_capacity` call before the loop. I am sad to see that clippy doesn't have any lints for this, but I can see how it would be hard to make a general good suggestion for it.
- talideon 6y agoKeep in mind that the potential expense of string concatenation was only _one_ example. There's a reason why I explicitly mentioned immutability: Rust's `String` type has more in common with C#'s `StringBuilder` class than it does with strings in most languages. Meanwhile, the behavour of str/&str is very different.
- deleted 6y ago[deleted]
- pansa2 6y agoReminds me of this cheat sheet [0] for slices in Go, which includes a snippet to delete an element from a slice: a = append(a[:i], a[i+1:]...) [0] https://github.com/golang/go/wiki/SliceTricks https://github.com/golang/go/wiki/SliceTricks
- LukaD 6y agoWow I forgot how unergonomic go can be. And that example even is a potential memory leak...
- masklinn 6y ago> And that example even is a potential memory leak… Yup more generally a slice is not an actual vector, the backing array lives independently from the slice, meaning if you "reset" a slice a = a[:0] all the elements are still part of the backing array, and thus still referenced. That's also why write-sharing (which is trivial in Go) is super dangerous e.g. a := GetSlice() b := a[:] a = append(a, 1) b = append(b, 2) depending on the size of the backing array associated with slice `GetSlice` returns (aka the cap() of the slice), the last element of `a` might be 1 or 2.
- majewsky 6y agoUm, what? My impression was always that the last line would copy the underlying slice since someone has already appended to the original backing array. That's the whole point of why append is not in-place. You get a new slice value back because the extension may have happened by copying data into a new backing array.
- masklinn 6y ago> My impression was always that the last line would copy the underlying slice since someone has already appended to the original backing array. Nope. append only copies if there’s no room in the backing array, if there is it will just extend the slice in-place and set the new elements. So if there’s room, a will be extended in-place, with the new element set, then b will be extended in-place, overwriting the element set by a. Hell, how would append even know that somebody else appended to the backing array? As far is it can see that might as well be leftover garbage. > You get a new slice value back because the extension may have happened by copying data into a new backing array. That only happens if the backing array is full aka len() + new > cap().
- s_brady 6y agoI really want to like Rust, I like the intention of what the language is trying to do. But in a universe where languages like Smalltalk, Eiffel and Lisp exist, it just feels like a step backwards. Surely the computer should be doing the heavy lifting? What happened to my bicycle for the mind? Just my opinion. I admire the work going on in Rust but the complexity makes me sad.
- de6u99er 6y agoSmalltalk is still a thing?
- pjmlp 6y agoYes, https://pharo.org/ https://pharo.org/ As for commercial versions, https://www.cincomsmalltalk.com/main https://www.cincomsmalltalk.com/main https://www.exept.de/en/smalltalk-x.html https://www.exept.de/en/smalltalk-x.html https://www.instantiations.com/vast-platform https://www.instantiations.com/vast-platform And its IDE spirit lives on Java IDEs and Visual Studio Code (as Erich Gamma is on the team).
- pjmlp 6y agoThis is why while I toy with Rust, I can only see it replacing C and C++ (long term), in the context of kernels, device drivers and real time scenarios where any kind of memory allocation is a no-go (MISRA style). For anything else, it isn't worth the productivity drop of automated memory management (regardless in what form) coupled with value types.
- mxcrossb 6y agoI’m pretty sure that’s the point. But since it’s trendy people try to apply it to everything.
- hu3 6y agoSome go as far as saying that "Rust is pretty accessible to developers coming from scripting languages (JavaScript, Python, etc)." [1]. Meanwhile in the real world: "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." [2] [1] https://news.ycombinator.com/item?id=26929012 https://news.ycombinator.com/item?id=26929012 [2] https://news.ycombinator.com/item?id=23807574 https://news.ycombinator.com/item?id=23807574
- oblio 6y agoI'm not sure I understand why format is a macro and print and equivalent in most other programming languages is a regular function. There's some complexity there that I guess I just don't know. For the second part, Rust should probably add some sugar à la Python f-strings (or just plain old string interpolation dating back to the Bourne shell): format!("{x}{y}")
- CryZe 6y agoThis is actually planned already. Not sure how far along the implementation and progress to stabilization is though.
- oblio 6y agoWhen you say planned, do you mean it's in some roadmap? I couldn't find anything after a quick googling session. Edit: Found the RFC: https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html https://rust-lang.github.io/rfcs/2795-format-args-implicit-i... - but I don't see any sign that it has been accepted :-? Edit II: I had missed the PR and issue at the top :-)
- burntsushi 6y ago> There's some complexity there that I guess I just don't know. It's because the format string itself is checked to be consistent with the types of arguments given. If there's an inconsistency, you get a compile time error. Most printf-style functions in other programming languages don't do this.
- rascul 6y ago17 different ways to concatenate strings in rust, with benchmarks: https://github.com/hoodie/concatenation_benchmarks-rs https://github.com/hoodie/concatenation_benchmarks-rs
- tialaramex 6y agoSummary: There are several ways to concatenate strings in Rust. The method listed (using the format! macro) is one of the slowest but also most flexible, it will cheerfully concatenate any things that either are, or present an ability to be expressed as, strings. The very fastest "method" is to just tell Rust you're quite confident that it will be OK to assume all these strings happen to be contiguous in memory anyway and so it can just go ahead and assemble that memory into one string. Rust won't let you pretend it said this was safe, and sure enough in practice it will almost invariably break, but this is often what equivalent C code is trying for, it genuinely is fast, and if you really are confident you can arrange for the assumption to be true Rust lets you express that in an unsafe block so you get that same performance as C.
- willtim 6y agoWhilst it may appear that concatenation of strings is easier in other (more declarative) languages, in practice, understanding the runtime performance/complexity and memory usage/lifetimes is far from trivial. Sure, a more declarative language might still be preferable, but for those who need better control and more ability to reason about how the program with execute, the Rust approach makes sense.
- estebank 6y agoTo drive the point home on how strings work in Rust and what the pitfalls it addresses are, https://fasterthanli.me/articles/working-with-strings-in-rust https://fasterthanli.me/articles/working-with-strings-in-rus...
- richardanaya 6y agoCheck out https://tourofrust.com https://tourofrust.com
- estebank 6y agoTo be fair, when doing the naïve thing, the compiler will tell you what you need to do and (tersely) explain why it is needed: error[E0369]: cannot add `&str` to `&str` --> src/main.rs:4:15 | 4 | let z = x + y; | - ^ - &str | | | | | `+` cannot be used to concatenate two `&str` strings | &str | help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left | 4 | let z = x.to_owned() + y; | ^^^^^^^^^^^^ This used not to be the case and tripped people up. some even after being told why it is like this disagree that anyone should care. Rust is aiming at being performant. The thesis is that having a clarity on the actual behavior of code when reading it helps on this front. Because of this, you need to write more code than you would in other languages, while the upside is that when reading Rust code you can be fairly certain of what the code is doing in the final binary, without any thresholds at play changing the generated code from, for example, monomorphization to dynamic dispatch because you added a method or yet another instance. It's a trade-off. A language that didn't target the embedded and systems space could take all of Rust and radically simplify it, making it closer in experience and performance to Swift. That doesn't mean that the Rust project won't attempt to make things easier to use and write. A possible future move could be to make `Cow<str>` be the default string people actually use, with `&str` continuing to be the preferred argument and relegating `String`s to specialized cases, probably mostly data buffers.