News for hackers. Stuff that matters.
Re:Rust 1.17 by kevindqc on Thursday April 27, 2017 @01:41PM (#14213335)
> 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

Re:Rust 1.17 by pcwalton on Thursday April 27, 2017 @01:45PM (#14213364)
Because 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()".

Re:Rust 1.17 by moomin on Thursday April 27, 2017 @02:39PM (#14213750)
This 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.
Re:Rust 1.17 by zuzun on Thursday April 27, 2017 @04:51PM (#14214591)
Java 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.
Re:Rust 1.17 by vog on Thursday April 27, 2017 @05:56PM (#14215098)
> 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

Re:Rust 1.17 by twic on Friday April 28, 2017 @05:19AM (#14217992)
They 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/KtVB...

That said, I'm sure there are common cases it doesn't catch.

Re:Rust 1.17 by moomin on Friday April 28, 2017 @06:31AM (#14218266)
Not really, you've identified something where the compiler spots a special case pattern and rewrites it, something available to any compiler writer who is sufficiently chummy with the standard library. I'm not saying it isn't useful, but it's nothing like the runtime optimisations the OP was talking about. It can't even handle a for loop...

Closest real world example to a sufficiently smart compiler is GHC, and that can be plenty dumb at times.

Re:Rust 1.17 by vog on Friday April 28, 2017 @09:32AM (#14219186)
Another example is the query planner of a good relational database (PostgreSQL, etc.).

Here, SQL can also be viewed as a lazy evaulated programming language, due to its declarative nature and the fact that the execution order is not defined in the code, but instead an (hopefully optimal) execution order is determined by the query planner.

Re:Rust 1.17 by twic on Friday April 28, 2017 @04:58PM (#14222996)
> Not really, you've identified something where the compiler spots a special case pattern and rewrites it

Which, string handling being specifically optimised by the compiler, is exactly what was being discussed upthread?

Re:Rust 1.17 by moomin on Friday April 28, 2017 @07:27PM (#14224009)
What was being discussed upthread went a lot further than what you're talking about. The string concat thing is a specific piece of ordinary static analysis that any language with a compile stage can perform. It's also pretty trivially defeated in ways that crop up in real code. I'm not saying it isn't useful, I'm saying it's nowhere close to what would be required to render the performance costs of doing strings the Java way equivalent to the Rust way. Therefore, to go even further upthread, Rust still provides a meaningful choice, trading Java's convenience for Rust's performance.
Re:Rust 1.17 by mbrubeck on Thursday April 27, 2017 @02:20PM (#14213647)
It 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...

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.)

Re:Rust 1.17 by iopq on Thursday April 27, 2017 @11:21PM (#14216666)
It'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.
Re:Rust 1.17 by Rusky on Thursday April 27, 2017 @01:54PM (#14213432)
Rust'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.

Re:Rust 1.17 by epage on Thursday April 27, 2017 @02:22PM (#14213661)
This is actually being discussed on /r/rust, see 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-...

Re:Rust 1.17 by steveklabnik on Thursday April 27, 2017 @01:55PM (#14213449)
So 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.

Re:Rust 1.17 by eduren on Thursday April 27, 2017 @02:54PM (#14213839)
>palpatable

=palpable + palatable. Pedantry aside though, I like the word. Maybe it describes something can be palpably palatable.

Re:Rust 1.17 by steveklabnik on Thursday April 27, 2017 @05:26PM (#14214848)
Ha! Nice catch. Good thing I'm not like, a person who's paid to write words or anything...
Re:Rust 1.17 by chrismorgan on Thursday April 27, 2017 @06:36PM (#14215391)
You wrote a word! A new word!
Re:Rust 1.17 by MR_HANDS on Friday April 28, 2017 @07:07AM (#14218424)
or, palpatable could mean 'able to be palpated', which would mean able to be touched carefully with the intent of inspection, especially in medicine.
Re:Rust 1.17 by jononor on Thursday April 27, 2017 @02:33PM (#14213718)
For someone coming from C++, it will feel similar to std::string("foo") + "bar", which one gets used to pretty quickly.
Re:Rust 1.17 by Manishearth on Thursday April 27, 2017 @01:51PM (#14213404)
Because &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.

Re:Rust 1.17 by pc2g4d on Thursday April 27, 2017 @02:12PM (#14213584)
You 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.
Re:Rust 1.17 by Lev1a on Thursday April 27, 2017 @02:17PM (#14213622)
If 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.
Re:Rust 1.17 by steveklabnik on Thursday April 27, 2017 @02:18PM (#14213634)
In 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)

Re:Rust 1.17 by eximius on Thursday April 27, 2017 @02:20PM (#14213646)
I 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).
Re:Rust 1.17 by nercury on Thursday April 27, 2017 @01:54PM (#14213429)
It is fine, because you can't accidentally use modified string on the left.
Re:Rust 1.17 by humblebee on Thursday April 27, 2017 @02:40PM (#14213762)
Ah 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

Re:Rust 1.17 by baq on Thursday April 27, 2017 @04:51PM (#14214588)
webassembly is our hope. compile rust down to browser bytecode.

a man can dream.

Re:Rust 1.17 by vvanders on Thursday April 27, 2017 @01:43PM (#14213357)
It'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).
Re:Rust 1.17 by danieldk on Thursday April 27, 2017 @01:52PM (#14213415)
which 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.

Re:Rust 1.17 by vvanders on Thursday April 27, 2017 @01:55PM (#14213448)
Yeah, 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.

Re:Rust 1.17 by steveklabnik on Thursday April 27, 2017 @02:01PM (#14213493)
In 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!

Re:Rust 1.17 by vvanders on Thursday April 27, 2017 @02:06PM (#14213536)
Yeah, you'd find me in the latter camp for sure :).

Thanks for the details, it's appreciated.

Re:Rust 1.17 by jandrese on Thursday April 27, 2017 @01:51PM (#14213409)
This 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.
Re:Rust 1.17 by mbel on Thursday April 27, 2017 @03:26PM (#14214032)
You 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.