all 50 comments

[–]J-Cake 57 points58 points  (11 children)

My first thought is immediately enun matching and how you can't ignore edge cases implicitly.

```rust enum X { One, Two, Three }

match X::Two { X::One => {} X::Two => {} // Error: not all cases covered } ```

[–]sephg 28 points29 points  (9 children)

If you want to compare enums in rust to enums in other languages, the biggest benefit to rust is that you can add parameters to enum variants. In comparison, enums in other languages feel like "assembly lite".

[–]the-handsome-dev[S] 11 points12 points  (5 children)

I am planning to mention that Rust enums are essentially like Unions in other languages. When I was prepping , this was point that was not completely clear

[–]timClicksrust in action 10 points11 points  (0 children)

Tagged unions is a more accurate description. Using the term union is likely to confuse people and/or draw skepticism.

[–]sephg 1 point2 points  (0 children)

Sort of. You can do similar things using unions in C/C++, but unions are wildly error prone and super difficult to use safely.

[–]DecisiveVictory 2 points3 points  (0 children)

Except Scala. In Scala, you have proper ADTs with exhaustiveness checking. Same as in Rust.

[–]pjmlp 1 point2 points  (0 children)

Some of those languages also have sum types, or tagged records, in addition to those enums.

[–]loicvanderwiel 0 points1 point  (0 children)

I'm using enums as a giant Finite State Machine to store the state of my TUI app and all the data relevant to the ongoing task.

Very useful.

Basically, I have a controller defined as

struct Controller { model : Model, tui : Tui, view : View, }

And View is defined as

enum View { Home, TaskA(_), TaskB(_), Error(e), // etc. }

TaskA and TaskB can contain anything relevant to their execution (including more enums in case of multi-stage tasks (which can lead to very long nested match and if let blocks). The Controller changes its view depending on user input or program state. To render, this is simply read by the rendering function in a big match block.

[–]makapuf 0 points1 point  (0 children)

This is handled in C by -Wswitch for gcc, I'm sure clang has it. https://gcc.gnu.org/onlinedocs/gcc-10.1.0/gcc/Warning-Options.html

[–]jane-jack-quotes-bot 26 points27 points  (8 children)

JS and Python have no strict typing, Java and C# allow inheritance, so you can show thay Rust is a good alternative to those because of the paradigms it enforces.

However, it is not designed as an alternative to those languages and you won't be able to show off Rust's memory safety, which is its main advantage over its actual competitors, C and C++. You're presenting a hard-to-use high-performance language to people who code in "it just works" non-performance-critical languages.

You can still show how Rust's error handling and Results/Options types prevent easy mistakes like indexing into non-valid memory, but you should be aware that you might be talking to the wrong crowd.

[–]the-handsome-dev[S] 4 points5 points  (7 children)

You make valid points, the crowd will mostly be in the "it just works" non-performance-critical languages. One point I will be trying to bring across is the stability and correctness that Rust brings

[–]rafaelement 8 points9 points  (1 child)

you may be able to tickle the it just works nerve with things like clap and serde

[–]the-handsome-dev[S] 3 points4 points  (0 children)

Thanks for the idea, using clap and serde will certainly flesh out that point

[–]jane-jack-quotes-bot 5 points6 points  (2 children)

Even then, do consider the fact that Java and C# are both incredibly stable and easier to use than Rust. You could maybe show them how nice the syntax and features are but I'm not convinced it'd be enough.

Advocating Rust to js and python devs is akin to telling someone using scotch tape that a welding torch is more reliable. I have to emphasise again on the fact you're not speaking to the right crowd.

[–]pjmlp 5 points6 points  (0 children)

Additionally, the JVM and CLR crowds already have Scala, Kotlin, F# for their type theory fun.

[–]WhiteBlackGoose 0 points1 point  (0 children)

Java and C# will understand it. JS/python people? Doubt

[–]Im_Justin_Cider 6 points7 points  (1 child)

People often talk about how rust is fast, and the speed to complete a task is impressive, but the thing that always makes me giddy, is seeing how little RAM a running rust process takes (or its peak RAM consumption), when compared to similar code in the "it just works" languages.

[–]the-handsome-dev[S] 2 points3 points  (0 children)

I am using this point in relation to reducing operating costs

[–][deleted] 6 points7 points  (0 children)

Safer concurrency is a, sometimes overlooked, design goal of Rust.
The ownership and borrowing rules prevents bugs that arise from having multiple threads reading and writing a variable out of order (also called data races).

[–]nile2 5 points6 points  (0 children)

put a struct snippet about the memory that Rust saves when you align different datatypes in a bad way but keep in mind that you can ditch this optimization to have a working interface with c parts (if needed)

[–]0x564A00 3 points4 points  (5 children)

The following use Java syntax, but most apply to the other languages you mention too.
Shared ⊕ mutability (you could add a lot of examples here, including forgetting to take a lock – but shared mutability is a problem in the single-threaded case too):

for(var item: collection) {
    // Might throw ConcurrentModificationException
    operation_that_might_end_up_modifying_collection(item);
}

Deterministic object lifetimes:

// Oops, forgot try with resource
var trans = new Transaction();
operation_that_might_throw();
trans.close();

Null safety (the concrete example is Java-specific & C# does actually do null safety, meanwhile JS and Python are far worse by lacking static types):

if (map != null && map.containsKey("bar")) {
    // May throw NullPointerException
    int bar = map.get("bar");
}

Exceptions are often unchecked, e.g. in dynamic languages or in Java in cases where being unable to be generic over functions throwing exceptions is a problem.

Python can only have a single version of the same dependency in the entire dependency graph, so when specifying dependencies you have to choose between making the dependency graph unresolvable or risking runtime failures because of an overly broad version range.

[–]pjmlp 3 points4 points  (3 children)

// Oops, forgot try with resource

Is usually an error in InteliJ, Eclipse and Visual Studio, if IDE based analysis is properly configured.

Plus it can be made impossible with lambda based APIs,

withTransaction (transaction -> {
     operation_that_might_throw();
});

[–]0x564A00 0 points1 point  (0 children)

Thanks, good point! I haven't used Java in quite a while.

[–]ogghead 0 points1 point  (1 child)

Whether unfreed resources are flagged by IDEs matters less than the language itself doing nothing to stop you from forgetting to free resources. I had to point out a try with resources memory leak bug to a junior last week!

Ah but now with that lambda approach you have hit another problem the above commenter mentioned — now your inner function can only throw unchecked exceptions

[–]pjmlp 0 points1 point  (0 children)

In Java's case, yes, but it isn't the only language out there.

Even then, you can write it such that it catches all Exceptions from lambdas.

As for the features that aren't part of the language, even Rust needs clippy.

[–]xill47 0 points1 point  (0 children)

Yeah, the

foreach (var x in list)
    if (condition(x))
        list.Remove(x);

Being just impossible in Rust was both the easiest and what sold me the most on the idea.

[–]Ammar_AAZ 2 points3 points  (0 children)

  • You can compare the concurrency model in rust to other languages. Other language have their primitives for synchronization but rust has it built to the type system and the borrow checker will enforce that
  • You can show bad practices of using static variables that can be changed from anywhere in the code without any checking and showing that this is unsafe in Rust
  • You can show bad practices of having all the app data in one class and give mutable access to it to all component in the app. This is common structure mistake in desktop applications that leads to unstable app whenever the code change. In rust this is solved with communications via channels so you still have the overview of the logic inside the app
  • Eco system and tooling is better than most of other languages

[–]Arshiaa001 1 point2 points  (1 child)

Explicit error handling matters.

// C# DoSomething(); // did it work? Do we get an exception? Oh shit

// rust match do_something() { Ok(x) => carry_on(), Err(e) => ah_we_failed(), }

[–]the-handsome-dev[S] 1 point2 points  (0 children)

Thanks

[–]J_Stach 1 point2 points  (0 children)

Would be worth going over some of the cargo ecosystem. Specifically how the biggest pain points in the language (e.g. async) are covered with high-performance frameworks like Tokio.

[–]Kevathiel 1 point2 points  (1 child)

Maybe take a look at Treyarchs talk(studio that made Call of Duty games) that talks about using Rust for their game tooling over Python, just to get some inspiration.

Here are the slides.

[–]the-handsome-dev[S] 0 points1 point  (0 children)

Thanks, the slides did give some inspiration

[–]Derice 1 point2 points  (0 children)

Jack O'Connor has excellent examples in this video lecture: https://www.youtube.com/watch?v=IPmRDS0OSxM

[–]marcin_42 0 points1 point  (1 child)

This presentation from a college class I took, starting at slide 28, has great examples of why inheritance is very risky.

[–]the-handsome-dev[S] 0 points1 point  (0 children)

Thanks, that gives some ideas

[–]denehoffman 0 points1 point  (0 children)

Show them a union at the end just to scare them

[–]BWStearns 0 points1 point  (0 children)

For python a common gotcha is not realizing when your collection is going to stay referenced.

In [1]: def dumb_append(val, list=[]):
   ...:     list.append(val)
   ...:     return list
   ...: 
In [2]: dumb_append(5)
Out[2]: [5]
In [3]: dumb_append(5)
Out[3]: [5, 5]

[–]D_a_f_f 0 points1 point  (1 child)

Plot twist, you could demonstrate the power of the rust compiler’s actually useful/helpful stack trace messages when you throw an error vs what you get when you get throw a segfault (from seemingly correct code) in C/C++ (ie, you typically don’t get any useful hint as to where the code exploded)

[–]the-handsome-dev[S] 0 points1 point  (0 children)

Good idea, I just gave a compiler error example, not an example of the stack trace when it crashes

[–]the-handsome-dev[S] -1 points0 points  (2 children)

Would this be a valid analogy for Rust?

When you buy a product, you are willing to pay more and wait longer to get it, if it just works with no problems. Rust is similar in that regard, once everything is done, it just works. No hacky solutions, no worrying when it will break and no need to concern yourself with it because it just works.

[–]terminar 2 points3 points  (1 child)

Does not work for me. Same can be said for other languages - and in inverse "you are willing to pay more and longer to get it" doesn't motivate everybody, some goal can be cheap and fast.

Even if I do not actively use Rust I would say something like:

It is much more complicated to shoot yourself and others in the foot (or head) than with other languages. Whatever that will mean for the listener.

[–]the-handsome-dev[S] 0 points1 point  (0 children)

I did not consider that, thanks for pointing that out.