Hacker News new | past | comments | ask | show | jobs | submit login

Performance is quite a bit higher for rust. And it's a much safer language by design (and forces the programmer to be as such).

That said, development time in Python is much faster.

Honestly, it depends on what you're building.




Rust is not safer than Python in Rust's own terminology. It takes more work than in C, but you can write memory bugs in Rust, by design. Python will catch those damn near every time, and if it doesn't you file a bug. Sure, there's less serious classes of bugs that Rust is likely to catch, but that's not really safety except in a definition loosened to near uselessness.


> you can write memory bugs in Rust, by design

Unless you're talking about unsafe rust, that's wrong. And if you ARE talking about unsafe rust, well you can do the same in Python.

I do agree that python and rust are both "memory-safe" languages. I'm not sure about the other kind of safety rust provides though: thread-safety. That's really where Rust shines: you can write concurrent program and be statically guaranteed to have no data-races. I'm not sure how this transposes in python.


You can describe "safe" along different axes and python is not completely immune to memory issues. I guarantee that if you start using ctypes you'll manage it at some point.


I'm curious what parts of Rust you think are safer than Python.

Edit: The only big thing I can think of is the safety of compile-time type checking to ensure you won't end up with some kind of runtime error from mismatched types. Is there something I'm missing?


Immutability by default, exhaustiveness checks (make sure that you match over all variants of an enum or cover all valid values in an integer), return flow validation (did you forget a return somewhere? Is it the right type?), and, as you mentioned, type checking being mandatory, all libraries define their accepted types. Not only that, being types "cheap", it is customary to wrap base types in explicit types that won't be compiled:

    type Point(u32);
    fn foo(p: Point) {...}
    foo(0u32);
        ^^^^ expected Point, found u32


When you work with the type system it checks a lot more than you check in Python. In Python you pass around untyped tuples and maps because defining classes won't gain you anything (and is surprisingly cumbersome and unpythonic), whereas in an ML-family language like Rust you use lightweight, fine-grained types to check that every function call is correct and you can refactor fearlessly.

(I'd recommend using a language with garbage collection unless you really need to not though; OCaml is quite Rust-like but means you won't have to worry about the borrow checker)


> I'd recommend using a language with garbage collection unless you really need to not though; OCaml is quite Rust-like but means you won't have to worry about the borrow checker

OTOH, Rust arguably has a better story for tooling (build system, dependency management, etc.), a more full-featured standard library, and better concurrency support. Depending on your priorities, some or all of these might be worth having to learn the borrow checker.


Well, O'Caml and Haskell predates Rust by decades. I was delighted to see Sum-Product types in Rust when I played with it. They go a long way to cleanly model the problem domain. Obviously you can simulate them, but it's much easier to get wrong or "cheat" (say having a bunch of fields, only some of which are valid depending on a tag).


> I was delighted to see Sum-Product types in Rust when I played with it. They go a long way to cleanly model the problem domain.

Umm yeah, they've been a part of every ML-family language since the '70s, OCaml, Haskell and Rust included.


(FullyFunctional seems to understand this, as far as I can tell)


I do, but I wasn't aware that Rust was considered in the ML family (ML as Meta Language, like SML/NJ). I'm somewhat skeptical of that.

What I see from Rust is a lot of the really great things from functional languages (esp. strong typing) and beyond, but applying them to a language aiming as low as C. That's certainly interesting.


Rust also enforces deep immutability by default, which makes resource sharing bugs much less common.


the type-checking catches lots of memory errors (irrelevant in a GC language like Python, but wouldn't be caught by the compiler in C or C++), but it also catches data races when writing concurrent code, which is definitely something that could happen in Python.


> The only big thing I can think of is the safety of compile-time type checking to ensure you won't end up with some kind of runtime error from mismatched types. Is there something I'm missing?

It's this but designs in statically typed languages with more expressive type systems tend to push a lot of the logic into the type system so you can't use APIs incorrectly. This isn't exclusive to Rust but I do have a few examples:

The Glium OpenGL wrapper puts a lot of effort into moving errors to compile time. The overview[1] explains a bunch of them.

[1] https://github.com/glium/glium#why-should-i-use-glium-instea...

The Servo project uses the type system to tie rust code into the spidermonkey garbage collector so it can't be misused[2].

[2] https://research.mozilla.org/2014/08/26/javascript-servos-on...

More abstractly, one of the more unique features of Rust's type system (linear types) is the ability to be sure a reference is destroyed. This makes Rust particularly good at describing state machines that are compile-time checked. Using standard OOP terms, each state is a class and its methods are the valid transitions. You can't take an invalid transition because the method is missing. You can do this in any language, though it tends to be only done in statically typed languages and it's awkward in Python. What makes Rust unique is that calling the method will consume the instance so you can't accidentally make a transition twice. Trying to call the method again is a compile error. A concrete example of this is state_machine_future[3], which generates an asynchronous state machine using macros.

[3] https://github.com/fitzgen/state_machine_future#example

In a slightly different use of the type system, the Rocket framework has a concept called Request Guards that allow you to map from something in the Request to a parameter in the request handler function. The overview[4] has a simple example on the "Dynamic Params" tab that maps url pieces to a string and an int. This mapping, however, is extensible and you can map to anything. If your handler needs an AdminUser, you can have the request guard pull the user id off the request, connect to the db, retrieve the user, verify the user as an admin, and only enter the handler if all that is successful. This means you can move all the logic and error handling around this into a single place that can be reused just by putting an AdminUser parameter on a handler. I've seen this done with middleware in other frameworks but in Rocket it's on-demand per-handler. As a result, the handlers only have to implement the happy path, which keeps them more compact than I've seen in dynamic language frameworks.

[4] https://rocket.rs/overview/

So the general idea is to use the type system to help you use stuff right. As with anything, it's possible to overdo it and get crazy boilerplate heavy code where you have to convert/cast all over the place and unanticipated use cases can't be done but it tends to be helpful, particularly with autocomplete.


That's a big one.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: