8 ms·
> String concatenation appends the string on the right to the string on the left and may require reallocation. I don't really know Rust, but isn't that weird?
by kevindqc 9y ago
> String concatenation appends the string on the right to the string on the left and may require reallocation.
I don't really know Rust, but isn't that weird? In most languages I know, I would expect for example Vector3(1, 2, 3) + Vector3(2, 3, 4) to return a brand new Vector3(3, 5, 7), with Vector3(1, 2, 3) unmodified? Unless I do vector1 += Vector3(2, 3,4), at which point I expect vector1 to be modified without creating a new vector?
Why doesn't rust create a new String and concatenates using this new owned String? I guess that's why beginners get it wrong - I would
- vvanders 9y agoIt's because you don't have a String to begin with, just two const memory values(which most of the time are baked into the program binary in C/C++, not sure about Rust).
- danieldk 9y agowhich most of the time are baked into the program binary in C/C++, not sure about Rust Pretty much the same, string literals are of type &'static str, which is a string slice with a static lifetime. Since literals are not `mut`, you cannot mutate them.
- vvanders 9y agoYeah, semantically you're right. I'm talking about actual binary representation. Some systems/runtimes copy them to the heap/etc while others let you reference the actual memory in the loaded binary. Back when I did more embedded stuff we cared since it could increase your runtime memory by a non-trival amount depending on what the platform did.
- steveklabnik 9y agoIn Rust's case, to be clear, they're stored in .rodata. That's where the &'static str points to. The auto copy to the heap behavior is controversial, desired by many for ergonomic reasons, disliked by others for implicit heap allocation. We'll see if it gets added in the future or not!
- vvanders 9y agoYeah, you'd find me in the latter camp for sure :). Thanks for the details, it's appreciated.
- deleted 9y ago[deleted]
- pcwalton 9y agoBecause the left-hand side is consumed by the + operator, so its allocation can be reused. Most languages don't have the concept of ownership, so it works differently in other languages. If you want to get the semantics of other languages, just clone the left-hand side with ".clone()".
- moomin 9y agoThis is kind of exactly why Rust is worth having. There's plenty of other languages that do the same thing as Java, and it's the right choice for a lot of use cases. Rust, instead, goes for the memory efficient design that doesn't create junk on the heap. Not always the right choice, but well worth having the option.
- zuzun 9y agoJava is a bad example since it could (theoretically) do all kinds of optimizations based on runtime profiling under the hood. It also has features like deduplication, so your string might use no additional memory at all.
- vog 9y ago> Java is a bad example since it could (theoretically) do all kinds of optimizations based on runtime profiling under the hood Isn't that true for every language? No offense intended, but this strongly reminds me of the "Sufficiently Smart Compiler" [1] The Java community had more than enough time to implement that, especially compared to a young language like Rust, so there's probably a reason they don't have it yet. [1] http://wiki.c2.com/?SufficientlySmartCompiler http://wiki.c2.com/?SufficientlySmartCompiler
- twic 9y agoThey have implemented it. javac compiles chained string concatenation to a sequence of StringBuilder calls, and HotSpot knows how to specifically optimize those: https://groups.google.com/forum/m/#!topic/jvm-languages/KtVBYfeT83g https://groups.google.com/forum/m/#!topic/jvm-languages/KtVB... That said, I'm sure there are common cases it doesn't catch.
- Manishearth 9y agoBecause &str is just a string view. If you add vectors, you are adding owned types where you own the allocations. &str is not owned, but String is. We could make the addition operator implicitly allocate here. It's a bit of a philosophical question if implicit allocations like that are desirable in rust. I lean towards being okay with the implicit allocation.
- pc2g4d 9y agoYou could maybe introduce a lint saying "possibly unnecessary allocation" when people `+` `&str`s together. Then beginners can still use the syntax that they're familiar with (and that basically every other language uses) but there's still notification somewhere that they can likely structure their code to be more efficient.
- Lev1a 9y agoIf I understand the ownership rules correctly, it would actually violate those rules to have the "familiar syntax". Also it seems to be Rust's thing to make things explicit, e.g. `.clone()`, not having `++`-style operators etc.
- steveklabnik 9y agoIn this case, due to the rules, it would be possible; the call to clone would be inside the method call, so you wouldn't see it. (With other signatures it may not, but Add returns a value, not a reference.) In other words, it would violate the conventions, but not the rules. (Okay so there's a _really_ obscure rule here that sill prevents this but it only matters because it's the standard library. It's not actually the signature)
- eximius 9y agoI would definitely side with it being okay. I like how string are modeled, but it doesn't mean it isn't painful sometimes. Making some common string operations like &str + &str defined but linted is a good balance of ergonomics and efficiency (that'd be a rare bottleneck, but the lint would make it visible).
- jandrese 9y agoThis is how concat() works in the C library, except that in the case where the original string is too long it will overwrite random memory instead of reallocating the original string.
- mbel 9y agoYou probably mean strcat() (which should really not be used, since now there is strncat()), there is no concat() in standard C library. strcat() does not intercept syntax of operation that does not mutate its arguments in any other context (+), you also cannot do strcat("hello,", " world") since the first argument must be mutable.
- nercury 9y agoIt is fine, because you can't accidentally use modified string on the left.
- humblebee 9y agoAh yes, this is what I was a bit confused about. Rust's ownership system though makes it so, what would be a very subtle and annoying bug I'm sure, can't happen. The error is really helpful though. fn main () { let hello = "Hello".to_owned(); let world = "world"; let hello_world = hello + world; print!("{}, {}", hello_world, hello) } 4 | let hello_world = hello + world; | ----- value moved here 5 | print!("{}, {}", hello_world, hello) | ^^^^^ value used here after move = note: move occurs because `hello` has type `std::string::String`, which does not implement the `Copy` trait The docs are also pretty awesome as well [0]. They also point out most of this and list out all the types which implement the copy trait. > Copies happen implicitly, for example as part of an assignment y = x. The behavior of Copy is not overloadable; it is always a simple bit-wise copy. > Cloning is an explicit action, x.clone(). The implementation of Clone can provide any type-specific behavior necessary to duplicate values safely. For example, the implementation of Clone for String needs to copy the pointed-to string buffer in the heap. A simple bitwise copy of String values would merely copy the pointer, leading to a double free down the line. For this reason, String is Clone but not Copy. > Clone is a supertrait of Copy, so everything which is Copy must also implement Clone. If a type is Copy then its Clone implementation need only return *self (see the example above). I haven't really used Rust at all, but the more I look at it the more I want too. I probably will not be given the opportunity anytime soon though. Such is life in the world of frontend development. [0] https://doc.rust-lang.org/std/marker/trait.Copy.html https://doc.rust-lang.org/std/marker/trait.Copy.html
- baq 9y agowebassembly is our hope. compile rust down to browser bytecode. a man can dream.
- Rusky 9y agoRust's + implementation consumes the String on the left, so the default behavior doesn't do any allocation unless the string needs more capacity. This makes it impossible to "get it wrong" in the way you seem to be suspecting. The part beginners tend to get wrong is trying to add a &str to a &str, which just doesn't have a + implementation to begin with, so they get lost.
- steveklabnik 9y agoSo I should preface this by saying that the Add impl was generally controversial and almost not even implemented, but ended up happening. In a systems language, allocations matter. As such, people are generally really against hidden allocations in Rust, moreso than in other languages. This totally could have been done, but by requiring a String on the left, you are already in "this is heap allocated" land, and that is much more palpatable for Rust's audience.
- jononor 9y agoFor someone coming from C++, it will feel similar to std::string("foo") + "bar", which one gets used to pretty quickly.
- eduren 9y ago>palpatable =palpable + palatable. Pedantry aside though, I like the word. Maybe it describes something can be palpably palatable.
- steveklabnik 9y agoHa! Nice catch. Good thing I'm not like, a person who's paid to write words or anything...
- chrismorgan 9y agoYou wrote a word! A new word!
- MR_HANDS 9y agoor, palpatable could mean 'able to be palpated', which would mean able to be touched carefully with the intent of inspection, especially in medicine.
- mbrubeck 9y agoIt is possible that adding two str slices like ("a" + "b") could be implemented in the future to produce a new owned String, but there is a technical obstacle to doing this in the near term: https://www.reddit.com/r/rust/comments/67rjii/question_about_adding_strs_to_string_concatenate/ https://www.reddit.com/r/rust/comments/67rjii/question_about... The basic problem is that str slices exist in the "core" library, which can be used on targets without an allocator (for example bare-metal embedded systems), while the owned String type exists only in the "std" library, which requires an allocator. Rust has rules that prevent one library from stomping on another library's types in certain ways that could conflict with future API additions, and those apply to the standard libraries here. (One solution would be to allow the author of libraries A and B to declare that A is allowed to treat types from B as though they were its own.)
- iopq 9y agoIt's simple, just let string literals possibly refer to Strings if type inferences deems it necessary. Failing that, it should consider them 'static &strs. Kind of what happens for numerical literals right now.
- epage 9y agoThis is actually being discussed on /r/rust, see https://www.reddit.com/r/rust/comments/67rjii/question_about_adding_strs_to_string_concatenate/?st=j20qiumc&sh=4f6fe872 https://www.reddit.com/r/rust/comments/67rjii/question_about... Turns out my assumption (like C++, make expensive things more verbose) isn't the complete picture. The big blocker is a technical issue preventing `"" + ""` See https://github.com/rust-lang/rust/issues/39018#issuecomment-273276843 https://github.com/rust-lang/rust/issues/39018#issuecomment-...