Rust to Nim: A Comparison

Note that Ruby will do something similarly "unexpected" in the example mentioned above (which is not as contrived as you might imagine). Try running the following script:

s = "Hello πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦"

until s == ""
  puts s
  s.chop!
end

As stated above, this is not a special property of Rust, but a result of text encoding being more complicated than the English-centric programming world commonly expects.

Ruby does the naive thing (which might work for simple languages like most of those using the latin alphabet), while Rust correctly defers this responsibility to you, the programmer. If you decide you are fine with e.g. only supporting ASCII, you can use the bytes methods, whereas if you want to handle this correctly (as correctly as reasonably possible, that is), you can take a look at the unicode_segmentation crate.

From unicode.org:

The following are the recommended emoji zwj sequences , which use a U+200D ZERO WIDTH JOINER (ZWJ) to join the characters into a single glyph if available. When not available, the ZWJ characters are ignored and a fallback sequence of separate emoji is displayed.

:family_man_woman_girl_boy: is a single printable character only on on platforms where "available". A given platform may display it as :man::woman::girl::boy: or :family_man_woman_girl::boy: instead. That is to say, "last (printable) character" is not well defined in Unicode. Rust could always make a judgement call one way or another, always deleting the entire zwj sequence, or always treating zwj sequences as simply a sequence of separate emojis (like Ruby seems to do). I fully agree that the fact that Rust has not made such a judgement call and Ruby has is very indicative of a difference in philosophy between the two languages, but the problem is not as trivial as you describe.

The "problem" is what you're trying to solve for. Ruby gives you a simple way that works for 99% of users 99% of the time, but also gives methods to control for more customized needs (after all, Rails runs on Ruby!). But again, your instinct is to bring up edge cases to justify not changing anything. :slightly_frowning_face:

Here's another example. What do you think most people would prefer to write?

Ruby|Crystal

right_half = left_half.reverse

Nim

let right_half = left_half.reversed

Rust

let right_half = left_half.chars().rev().collect::<String>();

Only a sado-masochist would prefer to do this simple thing the Rust Way!
Why couldn't you (don't you) wrap that code in a macro and make it easy to use?

Rust still has the feel of an academic project trying to implement new|novel CS ideas, without any regard to creating a polished product for general use by consumers (real people). It's still being controlled by software engineers, but needs people with others skills (linguists, artists, writers, etc) who can transform its technology into a prettier and easier to use product.

No car company would let its engineering department design how its vehicles look and feel. Most people buy car because of how they look, and how they feel when they drive it, not because of their underlying technology. Most cars now are sold with automatic transmissions because most people don't want to manually shift gears themselves.

Rust is a car that forces you to do everything manually to operate it, which isn't pretty, comes with a high cost (steep learning curve), and isn't worth it for most people to bother to use, compared to other car alternatives.

Let me make a prediction.

The mentality of the current controllers of Rust will not take it to where it needs to be to make it a polished language for people to use. Thus, either others will take its good concepts and package them into a better language or new people will be recruited into Rust to do that internally. Maybe then, by Rust 3.x, it will be that polished vehicle, that looks good, that's easy and fun to drive, that doesn't come with a premium price.

Another thing you need to appreciate too is other languages aren't standing still!

Ruby 3.0 is scheduled to be released on Christmas 2020 (as traditionally done). It is designed to be at least 3x faster than Ruby 2.0, comes with a parallel threading model, provides for static typing, while being non-breaking to old code. And Crystal's next release is scheduled to be its 1.0.

I would urge you to take seriously the need to make programmer productivity a high priority. Modern hardware makes most languages fast enough to use for most things. The differentiator then will be which language can you get the most done the quickest for your use case. There are still more Toyota Corollas sold than Teslas.

I am not a "sado-masochist" and I greatly prefer the Rust way, because reversing a Unicode string is not in fact simple. Collecting the code points in reverse order is almost always the wrong thing to do, so Rust makes you ask for it explicitly.

See this crate from @mbrubeck for a less-wrong way to do it:

I'm not sure what you are getting at there. What does Ruby actually do when a user uses the simple way that works for 99% of the time on a case that is in the 1%?

Does it silently produce the wrong result? Does it immediately exit with some fatal error? What? Perhaps you could offer a simple example we could run to demonstrate?

Personally I don't want my programs to silently produce wrong results when I accidentally use it wrongly. Cough, C, cough, Javascript, cough, many other languages. I want it to point out my error at compile time or abort at run time before the error propagates elsewhere.

This is a primary reason I choose to get into Rust and I have not regretted my decision in one year of extensive use.

I desperately hope that Rust never looses the values that guide it's development. Which happen to coincide with my values. I don't want to see Rust mutated into the dogs dinner of a sloppy language you are promoting.

By the way, I prefer manual gear shifts in my cars as well :slight_smile:

In general, I think it's fair to say that Rust's ethos prioritizes correctness, and consequently the language will usually not give you an interface that claims to be a general-purpose solution to some problem unless it really is always correct. If it's only correct "for 99% of users 99% of the time", Rust will make you explicitly opt in to a partially-correct interface. Reasonable people can disagree about whether that's a good approach; I think it is.

In my everyday life, I'm basically never called upon to write something with the letters in reversed order. The closest I can think of is the rare times I've needed a mirrored image that contained some text. As such, it seems to be a poor choice for examining the relative merits of programming languages-- Other text-based operations like parsing, templating, and displaying are more representative of typical programming tasks.

You are missing the point of what I want you to focus on.

The issue is not about what you think of Rust, and how you feel, it's about WHAT OTHER PEOPLE THINK AND FEEL!! I'm raising issues about the macro perception by non-Rust users of how the language operates, and looks and feels.

This is a false dichotomy continually promoted; it's either easy to do and incorrect, or correct to do, thus harder. That's not empirical. There is no technical impediment from doing things correctly AND EASY! It's a mindset to see them as two opposing outcomes that prevents doing the work to combine them together. In other words, you have to be willing to imagine how to make it happen, then work to achieve it.

Yes, you are right. And I'm challenging you all to rethink why your approach is the only right way.

Moderator note: We can compare languages without SHOUTING and calling people narcissists.

The closest to a correct and easy way is probably unicode_reverse::reverse_grapheme_clusters_in_place. It could technically be included in the standard library, but here we run into another part of the Rust philosophy: preferring a simple std. String reversing is pretty niche outside programming exercises and including complex grapheme-handling logic in std to support it is a trade-off that is seen as not worth it.

I just don't understand comments like this, knowing there are all kinds of programs that strictly manipulate text (LibreOffice, Vim, Emacs, etc, etc, etc).

FYI. One projects sees it to be worthwhile to make Rust<->Ruby interaction easy.
I guess instead of asking Why?, they ask Why Not?

Apparently they know how to make string reversal using Rust easy for users.

#[macro_use]
extern crate rutie;

use rutie::{Class, Object, RString, VM};

class!(RutieExample);

methods!(
    RutieExample,
    _rtself,

    fn pub_reverse(input: RString) -> RString {
        let ruby_string = input.
          map_err(|e| VM::raise_ex(e) ).
          unwrap();

        RString::new_utf8(
          &ruby_string.
          to_string().
          chars().
          rev().
          collect::<String>()
        )
    }
);

#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn Init_rutie_ruby_example() {
    Class::new("RutieExample", None).define(|klass| {
        klass.def_self("reverse", pub_reverse);
    });
}

Hmm... I am "other people". Or at least I was a year ago when I heard of Rust for the first time. I was attracted to Rust because it is the way it is, not the the way you are suggesting it be.

As Bryan Cantrill said when discussing his affinity to Rust it all comes down to "values". What does one value and do the values of the language developers align with ones own.

That sounds rather vague but our values influence what we do and the artifacts we create. Including programming languages. We all value different things differently. Which has a lot to do with the interminable discussions over "best" language.

As you quite rightly point out, the Rust approach is not the only "right way". The right way for any individual is determined by their values. Rust takes one of those right ways. Trying to combine all the right ways into a single programming language so as to satisfy everyone's values is likely to result in a horrible mess. If it is even possible at all.

Anyway, not to worry, there are hundreds of programming languages out there, with more popping up everyday, I'm sure you can find one that satisfies your particular set of values.

This is good news. Given that it can be made so easy to do in Rust with only a few lines of code all that is needed is to have a crate that provides that few lines of code.

No need for anything in Rust the language itself to change. No need for it to be in Rust std::

Text manipulation is not niche, but reversing Unicode strings is.

There have been multiples threads in this forum about how to make Rust easier to use. I, like others who started those threads, appreciate Rust's thrust to make memory use safe, but also know that ain't the most important thing to most programmers.

I say the things I say because I have used Rust to do major stuff with it.
Below is the fastest (so far) implementation of a twinprimes_sieve.

I also did this algorithm in D, Nim, and Crystal (someone else did a Java version), because the only way to know how to use a language is to program in the language. So I have an empirical basis to compare languages from.

My only reason for creating this thread was to encourage the people who control Rust development to look at the Big Picture of its use, and realize it can (should) be made better in areas besides technical|mechanical operation.

Unfortunately, people have not been willing to engage in the spirit of that discussion, to even begin to imagine what that could possibly look like.

So, I feel no need to continue this (futile) discussion.

Again, I think others, like with Rutie, will have the vision to do what I'm suggesting. Maybe down the road transpiler languages (like TypeScript for JavaScript) will emerge for Rust, say an EZRust (Easy Rust). And history teaches us, technology ALWAYS advances toward cheaper and easier to use.

I wish Rust well, but I feel like I don't have anything else I need to say on this topic.

I would not underestimate what "people", whoever you mean, imagine. As far as I can tell Rust developers and users are very experienced and knowledgeable and have backgrounds in all kind of other programming languages. I myself have used more languages, of many kinds, over more decades than I care to remember. We are not all running around blindly with blinkers on.

I can understand the reluctance to engage in the discussion. These these things always devolve into a "language war" with everyone shouting their preferences for this and that and trying to justify what they want by whatever means they can. Such language debates have been going on everywhere for decades.

As you say, it's futile.

This is the second time you've posted this algorithm (the other was in post 27), but while they might have provided something easy, they've not provided something correct.

As a simple example, with the holidays coming soon, try this:

fn main() {
    let s = "Noël";
    println!("{}", s);
    let s = s.chars().rev().collect::<String>();
    println!("{}", s);
}

Over-simplification in exchange for convenience, eh? Bit of a rant below - probably too ignorant and not good for my first post here. Feel free to remove, but this thread seemed like an apt place.

If anything, end-users who work with data - in this case text - need to have more low-level knowledge, not less. Unicode is tricky. Utf-8 is tricky. Pre-unicode (well, pre-utf8), I think I had to to work with three (?) different local encodings for Japanese ca 2005. I'd take Utf-16 without a BOM over that.

Personal opinion, but I feel we are losing important knowledge in exchange for a bit of convenience far too often with digital data. And in my experience, digital data is the flimsiest, most fragile data form humanity has ever created. Practical? Absolutely, but cave paintings will probably outlive the recorded history of humanity post 1975 (or whatever the break-off point may be) if this kind of knowledge becomes that of the few. "Hey, my code flipped a bit in some random place and now my data is unreadable", vs "I smudged out one corner of this cave painting, but it still looks ok" or "The tape of this open reel broke, but we'll just glue it back together".

I work in academia (humanities) and sometimes it feels as if there's no end to the [unintended] malformed textual data that is used for models, statistics and to publish actual results on. This includes, but is not limited to, going from one encoding to utf-8 without checking that the output is actually correct to the human brain (gibberish may still be welformed utf-8, a computer doesn't care), to complete lack of understanding how bytes/code units/code points/glyphs/graphemes/what-have-you differ. As already concluded, it's complex.

Outside of academia, didn't someone exploit Github's normalisation-by-lowercase for user names by using low-frequency graphemes that were then "lowercased" into more common ones (e.g. lowercase isn't applicable to IPA, apparently some algorithms do not care)?

I do agree that string reversal is not that niche and absolutely has uses in my sector. But if the researcher does not take the care to do this properly it will produce the wrong results. Not unexpected, that may be a valid result, but wrong because the input was malformed. Perhaps it's just academia that's sometimes behind?

The more I get involved with data processing, the more I've come to appreciate Rust's stance on many things. But, sure I too reach for Python sometimes.

To beat a dead :racehorse:, the naΓ―ve way of reversing characters will also turn (for example) Australia πŸ‡¦πŸ‡Ί into Ukraine πŸ‡ΊπŸ‡¦ on platforms that render flag sequences. This is somewhat more subtle than the πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦ example because flag sequences do not use zero-width joiners; the order of the code points is the only difference between the two graphemes. Furthermore, unlike the Noël example, flag sequences are never affected by normalization (so converting first to NFC wouldn't help (not that you should rely on that anyway)).

Wait I assumed I knew what to expect for the Noël example, but it comes out as (the weird output is from a stupid cough Julia script I put together):

STRING: lΓ«oN
---
GRAPHEMES [4]:
(SubString{String}["l", "Γ«", "o", "N"])
---
CODE POINTS [4]:
['l', 'Γ«', 'o', 'N']

'l': ASCII/Unicode U+006C (category Ll: Letter, lowercase)nothing
'Γ«': Unicode U+00EB (category Ll: Letter, lowercase)nothing
'o': ASCII/Unicode U+006F (category Ll: Letter, lowercase)nothing
'N': ASCII/Unicode U+004E (category Lu: Letter, uppercase)nothing
---
BYTES [5]:
UInt8[0x6c, 0xc3, 0xab, 0x6f, 0x4e]

BITS:
01101100
11000011
10101011
01101111
01001110

Perhaps my browser (Safari) is partly to blame? [EDIT: Yes, Firefox does what I expected]

This playground example (same code with the string "Kɔ́ɔn") is more in line what I expected, the diacritic is in the wrong place, since it's a simple code point reversal, not taking the order of non-spacing marks into account .

In regards to flags changing nationality, I now want to find the most offensive "nation reversals" (if there are other cases like that, that is). (EDIT: Sorry, I didn't intend to sound political, just though it was a funny coincidence)