Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> I think Rust is harder to learn, but once you grok it, I don't think it's harder to use, or at least to use correctly. It's hard to write correct C because the standard tooling doesn't give you much help beyond `-Wall`.

When I say Rust is harder to use (even after learning it decently well), what I mean is that it's still easier to write a pile of C code and get it to compile than it is to write a pile of Rust code and get it to compile.

The important difference is that the easier-written C code will have a bunch of bugs in it than the Rust code will. I think that's what I mean when I say Rust is harder to use, but I'm more productive in it: I have to do so much less debugging when writing Rust, and writing and debugging C code is more difficult and takes up more time than writing the Rust code (and doing whatever less debugging is necessary there).

> Also, I highly recommend getting into the habit of running `cargo clippy` regularly. It's a wonderful tool for catching non-idiomatic code.

That's a great tip, and I usually forget to do so. On a couple of my personal projects, I have a CI step that fails the build if there are any clippy messages, but I don't use it for most of my personal projects. I do have a `cargo fmt --check` in my pre-commit hooks, but I should add clippy to that as well.




If you're using VS Code then you can add `"rust-analyzer.check.command": "clippy"` to your `settings.json`. I assume there's a similar setting for rust-analyzer in other editors.


Neovim:

    require("lspconfig").rust_analyzer.setup({
        settings = {
            ["rust-analyzer"] = {
                checkOnSave = {
                    command = "clippy",
                    allFeatures = true,
                },
            },
        },
    })


You might want to reconsider use of rust-analyzer, it isn't safe to use on code you haven't written yourself.

https://rust-analyzer.github.io/book/security.html


> it isn't safe to use on code you haven't written yourself

Neither is cargo (nor npm, nor any other package manager, for that matter).

I'm not sure what value being that paranoid is buying you in the long run.


Package managers are for running other people's code, I would not expect the same of static analysis tools, especially since they are of use while auditing other people's code before building/running it.


Cargo's threat model here is identical to that of rust analyser. If you trust your dependency tree sufficiently to run `cargo build`, then you trust it sufficiently to run rust analyser.


Considering Cargo has build scripts, which are designed to run arbitrary code, that doesn't appear to be correct.

https://doc.rust-lang.org/cargo/reference/build-scripts.html


Rust analyser executes those exact same build scripts. This is the primary avenue for exploits in both pieces of software, and is called out explicitly in the page you originally linked.


Yes, and that is my complaint.


That's a fair distinction. Basically, it's easier to write C that compiles than Rust that compiles, but it's harder to write correct C than correct Rust.

Regarding Clippy, you can also crank it up with `cargo clippy -- -Wclippy::pedantic`. Some of the advice at that level gets a little suspect. Don't just blindly follow it. It offers some nice suggestions though, like:

  warning: long literal lacking separators
    --> src/main.rs:94:22
     |
  94 |             if num > 1000000000000 {
     |                      ^^^^^^^^^^^^^ help: consider: `1_000_000_000_000`
     |
that you don't get by default.


You can also add #![warn(clippy::all, clippy::pedantic)] to your main.rs/lib.rs file to get those lints project-wide.


The truly pedantic setting here would by complaining about the magic number.

Why 1_000_000_000_000, what does that number mean.

It is for free to:

    let my_special_thing = 1_000_000_000_000
since the compiler will just inline it.

The readability problem was never the lack of separators, since that number might be the wrong number regardless.


Getting something to compile is never the end-goal. It takes 0 effort to get Python compile.


It can feel like a real milestone though


> it's still easier to write a pile of C code and get it to compile than it is to write a pile of Rust code and get it to compile.

As someone who is more familiar with Rust than C: only if you grok the C build system(s). For me, getting C to build at all (esp. if I want to split it up into multiple files or use any kind of external library) is much more difficult than doing the same in Rust.


I’d add that the Rust code and C code will probably have the same number of bugs. The C code will likely have some vulnerabilities on top of those.

Rust doesn’t magically make the vast majority of bugs go away. Most of bugs are entirely portable!


Rust allows to provide more information about types (generic types, pointer usage) and checks it, while in C you have to rely on doc comments and checking the code manually. Or am I wrong and C allows to specify pointer nullability, pointer ownership and array bounds?


None of those things feature in any problem I deal with on a daily basis, whatever language I use.

So for example today I dealt with a synchronization issue. This turned out to not be a code bug but a human misunderstanding of a protocol specification saga, which was not possible to code into a type system of any sort. The day before was a constraint network specification error. In both cases the code was entirely irrelevant to the problem.

Literally all I deal with are human problems.

My point is Rust doesn't help with these at all, however clever you get. It is no different to C, but C will give you a superset of vulnerabilities on top of that.

Fundamentally Rust solves no problems I have. Because the problems that matter are human ones. We are too obsessed with the microscopic problems of programming languages and type systems and not concentrating on making quality software which is far more than just "Rust makes all my problems go away" because it doesn't. It kills a small class of problems which aren't relevant to a lot of domains.

(incidentally the problems above are implemented in a subset of c++)


> So for example today I dealt with a synchronization issue. This turned out to not be a code bug but a human misunderstanding of a protocol specification saga, which was not possible to code into a type system of any sort.

Maybe not in a reasonable language no, but there are advances in type systems that are making ever larger classes of behaviours encodable into types. For example, algebraic effects (can this function throw, call a remote service etc)

https://koka-lang.github.io/koka/doc/index.html

linear types (this method must be called only once etc)

https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/line...

dependent typing (f(1) returning a different type from f(2), verifiable at compile time),

https://fstar-lang.org/index.html

Some of these features will eventually make it to “normal” PL. For example, Scala now has dependent types,

https://dotty.epfl.ch/docs/reference/new-types/match-types.h...

and Java can support linear type checking,

https://checkerframework.org/manual/#must-call-checker


> None of those things feature in any problem I deal with on a daily basis, whatever language I use.'

I run into those things nearly daily, so... ok then.


Do not feel bad friend, It's not you.. there is definitely inconsistencies here. Maybe they work on absolutely pristine perfect codebases.


Vulnerabilities are bugs, so the C code will have more bugs than the Rust program.

You might say that the C and Rust code will have the same number of logic errors, but I'm not convinced that's the case either. Sure, if you just directly translate the C to Rust, maybe. But if you rewrite the C program in Rust while making good use of Rust's type system, it's likely you'll have fewer logic errors in the Rust code as well.

Rust has other nice features that will help avoid bugs you might write in a C program, like most Result-returning functions in the stdlib being marked #[must_use], or match expressions being exhaustive, to name a couple things.


> most Result-returning functions in the stdlib being marked #[must_use]

Actually it's a bit cleverer than that, and some people might benefit from knowing this. The Result type itself is marked #[must_use]. If you're writing a Goat library and you are confident that just discarding a Goat is almost always a mistake regardless of the context in which they got a Goat you too should mark your Goat type #[must_use = "this `Goat` should be handled properly according to the Holy Laws of the Amazing Goat God"] and now everybody is required to do that or explicitly opt out even for their own Goat code.

Obviously don't do this for types which you can imagine reasonable people might actually discard, only the ones where every discard is a weird special case.

Types I like in Rust which help you avoid writing errors the compiler itself couldn't possibly catch:

Duration - wait are these timeouts in seconds or milliseconds? It's different on Windows? What does zero mean, forever or instant ?

std::cmp::Ordering - this Doodad is Less than the other one

OwnedFd - it's "just" a file descriptor, in C this would be an integer, except, this is always a file descriptor, it can't be "oops, we didn't open a file" or the count of lines, or anything else, we can't Add these together because that's nonsense, they're not really integers at all.


I'd say whether Rust helps you reduce bugs depends on how good you are at creating abstractions and/or encoding properties in the type system.


Most bugs are way above that level of abstraction and thought.


[Citation needed].


Only about 70% of high severity bugs[1]. Quite a negligible amount.

[1]: https://www.chromium.org/Home/chromium-security/memory-safet...


This is objectively nonsense


Interesting use of the word 'objectively' there.


How can it not be true? One of the primary features of the rust compiler is enforcing memory safety at compile-time. C doesn't have anything like that. There are an entire class of bugs that are impossible to implement in rust.


>> One catches far more issues than another

> akshually they have same amount of bugs

You literally segfault by dereferencing invalid pointer.


I agree.

But: If you are LUCKY! The worse case is if you DON'T segfault by dereferencing invalid pointer.


Not really.

I have written a lot of C and Rust. The notion that identical projects written in both languages would have identical numbers of (or even severity of) bugs is laughable on its face.

I mean, literally just not being able to deref a NULL pointer by itself is enormous.


You can dereference null pointers in Rust, it's also easy to make null pointers. The thing is I'm not surprised you didn't realise this is possible because you would never actually do this in Rust since it's obviously a bad idea.

  let p: *const i32 = std::ptr::null();  // Pointer to a signed 32-bit integer, but it's null
  unsafe { some_local = *p };            // Dereferencing the null pointer, bang
If we instead use references they're nicer to work with, don't need unsafe, perform the same, and are never null. So obviously that's what real Rust programmers almost always do.


Of course it’s possible. I am well aware it’s possible. You can also just call into a C library that hands you a null pointer. Further, it’s wholly possible to invalidate every single one of Rust’s safety guarantees if you try hard enough.

In practice I have never encountered a null pointer in Rust. Most Rust programmers have never encountered a null pointer. For most non-FFI practical purposes you can act as if they don’t exist.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

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

Search: