One thing I don't like about Rust is how taking a slice of a string can cause a runtime panic if the start or end of the slice ends up intersecting a multi-byte UTF-8 char. I would prefer it if this feature didn't exist at all rather than cause runtime panics. https://play.rust-lang.org/?gist=e02ce5e9aacfee3a2b4917d5624...
It is a common pattern in Rust to use [] for things that cannot fail and will panic otherwise and a method for things that can fail and return Option or Result. e.g. my_hashmap["foo"] will panic at runtime if the key "foo" is not present, or return the associated value if it is. But my_hashmap.get("foo") will return None if "foo" is not present and Some(value) if it is.
Thoughts on Rust, a few thousand lines in
41–50 of 183 posts
Re: Thoughts on Rust, a few thousand lines in
#42One thing I don't like about Rust is how taking a slice of a string can cause a runtime panic if the start or end of the slice ends up intersecting a multi-byte UTF-8 char. I would prefer it if this feature didn't exist at all rather than cause runtime panics. https://play.rust-lang.org/?gist=e02ce5e9aacfee3a2b4917d5624...
String slicing using byte indices has to exist in some form, since it is the only thing that is efficient (O(1)). But, I guess it could have used syntax other than somestring[...].
Re: Thoughts on Rust, a few thousand lines in
#43Earlier quoted context omitted.
Does this bug exist because it would be too expensive to check every string before slicing? (Being Rust-ignorant), can you not type a binary as UTF-8? Are there 2 versions of string functions, fast ones that assume ASCII and slow ones that assume UTF-8?
Every string is checked. But UTF8 is a multi-byte encoding, and slicing works per bytes, so you if you slice in the middle of a multi-byte character, you may get nonsense. The error happens because of this checking, not in spite of it. String always assumes full UTF-8. You could make an AsciiString type if you wanted, but it's not provided by the standard library.
fn main() {
let a = "ab早".as_bytes();
let a = &a[..3];
println!("Hello, world!");
}Re: Thoughts on Rust, a few thousand lines in
#44Re: Thoughts on Rust, a few thousand lines in
#45Earlier quoted context omitted.
Yes, there is: https://en.wikipedia.org/wiki/IEEE_754#Total-ordering_predic... It is not something to use by default though. It is implemented in software and thus a lot slower than the hardware comparison.
I'm curious as to why it isn't implemented in hardware. Is it really so rare to need to sort floats, or so common to need a different ordering when you do?
But fortunately in comparison sort algorithms that run in O(n lg n) you can get away with doing an O(n) partitioning of the array into [-, +, NaN] and then applying a fast integer comparison operator to the negative values (-) and positive values (+).
In fact the above idea ties in neatly with QuickSort, which is already based on partitioning & sorting recursively.
Re: Thoughts on Rust, a few thousand lines in
#46Earlier quoted context omitted.
Every string is checked. But UTF8 is a multi-byte encoding, and slicing works per bytes, so you if you slice in the middle of a multi-byte character, you may get nonsense. The error happens because of this checking, not in spite of it. String always assumes full UTF-8. You could make an AsciiString type if you wanted, but it's not provided by the standard library.
Why not this? fn main() { let a = "ab早".as_bytes(); let a = &a[..3]; println!("Hello, world!"); }
Re: Thoughts on Rust, a few thousand lines in
#47Earlier quoted context omitted.
No, because x NaN are false for any value of x.
Yes, the literal < in the language induces a partial order by convention. What I'm getting at in my comment is that you can define a sensible total ordering.
So since no one needs this often enough to emit the soft-float comparison code, we should emit the fast code. If folks need different behavior they should use different types. This is similar to the behavior with integer overflow, which you can opt into by using checked types or checked operations. Though in rust we have a convenience that the overflow-detecting code is emitted for debug targets.
Re: Thoughts on Rust, a few thousand lines in
#48 let foo = "...";
let foo = parse(foo);
let foo = escaped(foo);
...
doSomethingWith(foo);
I don't see how this is helpful for avoiding the bug described. The most common bug with this type of code is mistaking which form "foo" represents at a given line of code, or that form changing as the code evolves. For example, if one programmer writes let foo = "...";
let foo = parse(foo);
...
doBarWithFoo(foo);
and another programmer comes along, doesn't notice the call to doBarWithFoo, and needs an escaped version of foo: let foo = "...";
let foo = parse(foo);
...
let foo = escaped(foo);
doBazWithFoo(foo);
...
doBarWithFoo(foo); // This still looks correct in isolation
This is a classic problem with mutable variables that frequently causes hard-to-spot bugs. Whereas if each form has a distinct meaningful name, this change wouldn't introduce a bug, and if somehow a bug were introduced, good names will make it possible to spot even considering the line in isolation: doBarWithFoo(fooEscaped); // Hey! The input to doBarWithFoo shouldn't be escaped!
If the only useful form of foo is the final one, then you can avoid having accessible names for the invalid intermediate forms like this: let foo = escaped(parsed("..."));
Or like this for more complicated logic (I'm not a Rust programmer (yet) so this may not be the right syntax): let foo = {
// Complicated logic
finalForm;
};
I feel like if I ever write a lot of code in Rust I'll find a linter rule that warns about shadowing variables and use it religiously. But maybe I'm missing a use case where it's crucial.Re: Thoughts on Rust, a few thousand lines in
#49> Like Go, Rust can compile statically linked linux binaries. The GNU C library (needed not only by C programs for C things) doesn't support static linking, so the only way this is possible is to use another library entirely, or raw inlined syscalls (where applicable).
Re: Thoughts on Rust, a few thousand lines in
#50Rust is one of the few times where the language/ecosystem does precisely what I wished it would do. For instance, being able to partially destructure JSON into a struct in a typesafe manner is just awesome.
Thanks in advance!