Live data from Hacker News

Rust vs C Pitfalls

garin.io

331–340 of 379 posts

Re: Rust vs C Pitfalls

#331

Earlier quoted context omitted.

That's not actually true though. You can use an atomic with a relaxed ordering. For example: https://is.gd/M1Q3Bu Now, to be fair, this is only one example and I'm sure you could come up with a better one. But this isn't especially controversial. Rust's entire standard library is living proof that you need unsafe to do some things efficiently. The value proposition is that such things can be bundled up behind an abst…

Thread locals are thread local, so you don't need AtomicUsize, you just need a Cell, which has zero overhead. https://is.gd/c0iCz6 Edit: Zero overhead as compared to C, that is. Cell has a slight overhead in Rust because it prevents certain optimizations that aren't the default in C.

Derp. Thanks for that correction. :-)

Re: Rust vs C Pitfalls

#332

Earlier quoted context omitted.

I gave you naive solution, now lets see amateurish solution (someone totally new to both languages) Rust 4.6s Go 3.1s http://pastebin.com/r6K22Dt2 EDIT: Using grep 0.6s Then I have installed rigrep and... Using ripgrep 0.4s Really nice burntsushi. I am surprised by those (ripgrep) results compared to grep.

Seems I can't reply to your other comment, so I'll reply here. How can you say that my naive implementation is not naive? What is not naive about it? It's very naive. It's basically the same naive code that you were writing, but in actual idiomatic Rust with the linting issues fixed. Using a `lines()` approach is naive because that allocates owned heap-allocated strings. An optimal, non-naive solution would not use h…

Your solution is not naive in my opinion because you set the size of the buffer and use map/filter but ok... Let's check. Your solution is the slowest from all the solutions.

It took 4.6s which only confirms what I wrote on beginning when we started this discussion. Perf counters for your solution in here:

http://pastebin.com/Anak1ahe

Re: Rust vs C Pitfalls

#333
post #309

Earlier quoted context omitted.

It isn't a "moral" issue. It is about the usefulness of the benchmarks. If you're using them to make a recommendation about which programming language to choose, it is important to have a sense for how well the results generalize to the kind of programs you will be writing. Using unrealistic hacks targeted specifically to each benchmark is contra that goal. If, on the other hand, you're just treating the benchmarks a…

Benchmark GAME :)

http://benchmarksgame.alioth.debian.org/sometimes-people-jus...

Re: Rust vs C Pitfalls

#334
post #307

Earlier quoted context omitted.

But people are criticizing C because the way it is implemented is not "realistic". Or that the "hash" is a hack that will never work in real life. I don't know the background of these people, but they couldn't be more wrong. So that looks like "morals" or just they don't ever saw real C code out there.

You're making an assumption that the C code is allowed to have the same algorithm as the Rust code. This is not actually exactly true, based on the rules of the game.

Where is it forbidden for programs written in C to use FNV?

For example https://github.com/haipome/fnv/blob/master/fnv.c

Re: Rust vs C Pitfalls

#335
post #281

Earlier quoted context omitted.

The problem isn't that it's a macro. It's that it's a horrifically bad hash function. It's blazing fast, but completely unsuitable for use in any real code.

If it's a hash function that only works for this exact data set, I can see the argument. If it's a hash function that works for this kind of data (these particularly formatted, particularly constrained strings), it's fair play. Which one is it? 'It's not a good general purpose hash function' alone doesn't seem like a valid criticism, especially along with 'the Rust version uses a general purpose hash function'. Nobod…

Sure, but it's not a good benchmark. If you can demonstrate that you have blazing fast performance, but only for the exact input data that the benchmark uses, then you haven't demonstrated anything at all beyond an ability to tailor your code to a very specific input. But in the real world, we don't get to write our code for a very specific input (after all, if we knew what the input was ahead of time, we could just pre-calculate the answers instead of publishing the program).

So yeah, if you can come up with a tailored hash algorithm that works correctly and is DoS-immune for all possible input for the program, go ahead and use that tailored hash algorithm. But if your hash algorithm is only correct for the specific input data the benchmark game uses, and would not be collision-resistant under real world conditions, then you really shouldn't use that hash as it does not demonstrate anything useful.

Re: Rust vs C Pitfalls

#336
post #316

Earlier quoted context omitted.

To be clear: anyone can contribute a Rust k-nucleotide program that uses a custom hash function, just like those used by other programming languages.

Wasn't there some issues around a "custom library" for this? I distinctly remember there being some kind of argument about what is legal and what isn't. That is, I think I'm thinking of this: > k-nucleotide will explicitly require built-in / library HashMap. https://alioth.debian.org/tracker/?func=detail&group_id=1008... Since C doesn't have a standard library hashmap, you can write an entirely custom one just for th…

The rules provided aren't as clear as they could be:

http://benchmarksgame.alioth.debian.org/u64q/knucleotide-des...

But they say 'don't write a custom hash table', not 'don't write a custom hash function'. Maybe the problem is that the data in this benchmark is just not a good way to exercise the hash tables in a given language the way the benchmark intended. That probably means the benchmark should be modified; the complaint that some implementations use 'laughably bad' hash functions that seem to be measurably decent hash functions for the data at hand seems really strange.

Re: Rust vs C Pitfalls

#337
post #69

Earlier quoted context omitted.

It has reference counting for some of the constructs which is a form of garbage collection and it has runtime overhead. Like shared_ptr in c++.

This is a misleading comment. A Rust program may use reference counting by explicitly wrapping some value in Rc or Arc ; the standard library provides these generic types. There are no "constructs" built in to the language or standard library that require the use of reference counting.

I wrote it's like shared_ptr which is also construct in C++ standard library, what is misleading here? You don't need to use it also in C++ and I did not wrote that you need to use it in Rust also, you disagree with something that I didn't wrote.

PS. Pozdrawiam rodaka.

Re: Rust vs C Pitfalls

#338

Earlier quoted context omitted.

You're only going to anger people by referring to another group as 'evangelists' when you yourself have already made note that you don't know enough about Rust to see why they are promoting it. There's much more to Rust than safety. You should actually spend a few months with it, and then give your opinion.

I certainly didn't mean to offend through my choice of words (many people seem to like the term "evangelist" nowadays), but it's not particularly realistic in an age of so many new languages to expect everyone to spend a few months on every single language just to form a basic opinion (that alone would be more than a full time job). Even the most polyglottic programmer has to pick and choose at least to some extent,…

A quick look at Rust's website and history should be more than enough to convince one to try it out versus other random programming languages with questionable backgrounds and ambitions. You have a large following that's backed by strong ambitions and companies like Mozilla and Samsung. It's always important to gain new perspectives and learn new concepts from languages that come by, as the ideas they teach are useful everywhere -- not just in the language of origin.

In any case, it's true that one shouldn't attempt to make an opinion about a technology without first being well informed with that technology, and that requires spending time with it to determine the validity of one's own hypothesis about it. Speaking down about it without having tried it is rather offensive to those who have spent the time to learn it.

Rust is strongly about memory and thread safety, which is a major plus. I don't see how that would cause it to be ignored by large swathes of developers when, in fact, that's what is causing large swathes of developers to make the transition. Even long time C houses like GNOME and Red Hat are calling out to stop writing future software in C, and to instead use Rust. GNOME's put their money where their mouth is and they are beginning to migrate their C codebases to Rust. librsvg, for example, is now written fully in Rust.

Rust's safeness allows me to feel comfortable working with really heavy optimizations that make extensive use of fat pointers everywhere to mitigate memory costs and keep everything on the stack. I can feel confident that if my solution compiles, it works. That Rust features test-driven development built into the language by merely adding a #[test] line above a test function to ensure that my logic is correct is even more comforting.

Yet there are many more arguments to make for Rust besides the memory safety. The memory safety is a side effect of the type system. The type system allows for more kinds of safety than just memory safety alone. It allows for compile-time checking of state machines, eliminating a large number of runtime checks at compile-time when you know how to take advantage of it.

Graphics APIs like OpenGL and Vulkan, for example, exhibit much cleaner APIs without the need for performing runtime checks to ensure that instructions are called in the right order, enabling faster execution out of the box.

Basically, taking a state by self will drop the original state, whereby you can return a new state in its place. Code completion will additionally aid the developer in only displaying methods that the current state supports. You can find examples in the hyper and vulkano crates.

In addition, Rust is heavy on the concept of data-oriented programming via protocol-oriented programming: traits featuring ad-hoc polymorphism akin to the likes of Haskell. This encourages more efficient programming practices than the object-oriented approach found in higher level languages like C++. Traits allow for a powerful form of generics whereby you can specify a range of input/output type parameters for all types that support the included traits.

The Iterator trait is by far one of my favorite features of Rust, and it is available without the standard library where it is absolutely vital. Basically, by implementing the Iterator trait for a type, which entails merely implementing the Iterator's next method, you gain access to all of the Iterator's adapters, which opens the door to all of Haskell's best features -- lazy functional programming, but without requiring a garbage collector and without using the heap. It boils down to very efficient machine code compared to if you had written it using a loop.

The sum types and pattern matching is also one of my favorite features of Rust. It's also key in the creation of powerful custom Iterators that may return multiple possible outputs, and it's one of the best parts of Rust's error handling strategy.

If a function may or may not return a value, then the return type is an Option which may either by Some(value) or None. Iterators return Options. If a function has a possibility of an error, then the return parameter is a Result which defines either Ok(value) or Err(error).

Enums may have their own parameters too, so pattern checking can become quite comprehensive to cover every possible result, which may have completely differing input parameters. One enum field could return an &str, another could return a usize, another could return an &str and a usize, and they can contain other enums with their own fields as well.

match result {

    Ok(Token::Argument(string) => (),

    Ok(Token::Placeholder => (),

    Err(TokenErr::IO(why)) => (),

    Err(TokenErr::NoArguments) => (),

    _ => (),
}

This then allows concepts like CoW smart pointers to be represented as an Cow::{Borrowed, Owned} enum in the standard library.

Anyway, language features aside, of which there's many more important features that Rust has to offer that'd take too long to explain, there's also the tooling that makes Rust powerful.

The rustup toolchain is one of my favorite things about installing and managing Rust on a system. It's trivial to install, and yet makes installation and updating stupid simple, regardless of your platform. I can easily tell others how to compile my software without them having to know anything about Rust or finding packages in a software repository.

rustup toolchain install nightly

rustup update (updates all toolchains)

rustup target add x86_64-unknown-linux-musl

rustup target add x86_64-pc-windows-gnu

rustup component add rust-docs

rustup component add rust-src

rustup run nightly cargo install racer

rustup run nightly cargo install clippy

cargo build --release --target x86_64-unknown-linux-musl

cargo build --release --target x86_64-pc-windows-gnu

rustup docs (opens offline version of Rust documentation website)

All from the same box. Cross-platform development made easy.

Then there's the powerful capabilities that cargo provides itself. It has a plethora of built-in subcommands, and exports a public API so that you may create and distribute additional subcommands. It automatically creates project hierarchies for you, even initializing git for you.

The Cargo.toml file allows you to define conditional compilation features for your project and libraries that you are importing. It allows you to define what dependencies that you want to pull and from where you want to pull them. Merely specifying a dependency name with a version number will have Cargo search Crates.io for the corresponding library and compile it when you build your project.

For binary projects, a Cargo.lock is provided which notes the exact version and hash of each library that you built to ensure that others that build your software will build it with the exact version libraries that you did when you released it. You only need concern yourself with dependencies on user systems when importing C libraries. If you build on Linux with the MUSL target, you can even build fully static binaries with zero dependencies, so long as you either don't use any C libraries, or build those libraries with MUSL.

Subcommands may even use their own tables in the Cargo.toml file to specify extra behavior, such as my cargo deb subcommand, which requires a few more fields to package Rust projects into Debian binary archives. Cargo provides many interesting subcommands, such as:

cargo build

cargo install

cargo run

cargo check

cargo update

cargo publish

cargo search

cargo doc

cargo edit

cargo test

cargo bench

cargo flame

cargo profile

cargo watch

cargo deb

cargo rpm

and probably more that I'm not yet aware of..

There's much to be said of Rust's community and documentation as well. Never before has a language had such comprehensive and ambitious effort as Rust into establishing an official community with a wide range of community resources. There's an official Rust Docs team that's continuing to add more documentation and examples for Rust and top crates in the Rust ecosystem; then there's a team dedicated to authoring official markdown-based books to teach Rust as the go-to free, printable book for Rust; there's an official Reddit thread, an internals discourse forum, a users forum, multiple IRC channels, and apparently quite a number of key developers browse through and comment on this domain as well.

Re: Rust vs C Pitfalls

#339

Earlier quoted context omitted.

> Rust had the chance to make strings feel as comfortable as integers, but instead they introduced their own dichotomy with String and &str. It makes perfect sense when you understand the differences and reasoning behind it. A `String` is a heap-allocated string that can grow in size. On the other hand, a `str` is basically a fixed-size string array, but you'll never interact directly with this type because there's n…

No, the default integer is u32. If type inference can't provide a better type, Rust will pick u32. If type inference can pick a better type it will use it. So if you create an unsuffixed int literal and use it for indexing, that literal will be a usize. Rust will type error if you try to use u32s with an array so it's all good though. Just means that you need to specifically `: usize` things. If you need an integer t…

That makes sense. I've never noticed what the default size was, but because it always worked with indexing, I assumed that it was always usize.

Re: Rust vs C Pitfalls

#340
post #281

Earlier quoted context omitted.

If it's a hash function that only works for this exact data set, I can see the argument. If it's a hash function that works for this kind of data (these particularly formatted, particularly constrained strings), it's fair play. Which one is it? 'It's not a good general purpose hash function' alone doesn't seem like a valid criticism, especially along with 'the Rust version uses a general purpose hash function'. Nobod…

Sure, but it's not a good benchmark. If you can demonstrate that you have blazing fast performance, but only for the exact input data that the benchmark uses , then you haven't demonstrated anything at all beyond an ability to tailor your code to a very specific input. But in the real world, we don't get to write our code for a very specific input (after all, if we knew what the input was ahead of time, we could just…

Well, here's the thing. I don't think it's for the exact input, it's for a type of inputs. Custom hash functions for specific types of data are a basic optimization technique and I find it odd you'd even suggest every hash function should be 'DoS-immune'. There's absolutely nothing 'real world' about this unless you think the world consists entirely of hostile inputs. In the real world, people absolutely optimize.

Your argument seems to be that that's not the intent of the benchmark which may be true but it's not clear from the rules provided at all. To me, it looks like the opposite is true - they talk about using a standard hash table and most of those allow user-specified hash functions.

Post reply on HN