HNHacker News
TopNewBestAskShowJobs

mmstick

178 karma · joined December 27, 2016

submissionscomments
mmstick··on Rust is mostly safety
> add nightly builds to distributions

rustup run nightly cargo build --release

You were saying? In any case, there's no such thing as a stable vs nightly split. There's pretty much zero libraries that require nightly, and the few that have a nightly option for optional features will no longer require nightly after the macros update lands.

mmstick··on Rust is mostly safety
Mind sharing the source code? I can whip out a Rust version in the same amount of time. I have been using Rust for two years. I never worry about the borrow checker as it never gives me problems.
mmstick··on Rust is more than safety
There is no cost associated with Result/Option when no error is returned. The compiler is smart enough to understand when failure is not an option. Only when an error occurs does Rust act on it.

Additionally, it's nonsense to state that you don't want to handle errors when they occur, but want to write software that can handle failures at any point.

Software libraries should handle their errors properly in the event that an error is possible in a function so that users of that library can handle them. Binary applications should handle errors when they occur versus randomly throwing them into the air and hoping for the best.

The `?` operator is highly convenient in handling errors wherever they may occur whereby the error needs to be handled. You do not have to use the `?` operator if have other means of handling the error. In example, maybe you want to use a default value in the event of a failure:

let value = attempt_value().unwrap_or(default);

Or maybe you want to keep trying until it succeeds:

let value = loop {

    if let Some(value) = attempt_value() { break value }

    thread:sleep(Duration::from_millis(1));
};

It's up to you how you handle your errors. The Option and Result types have a lot of methods for handling, such as and_then and or.

mmstick··on Rust is more than safety
In addition, Rust Results/Options are free when no throw occurs. So the argument is moot.
mmstick··on Rust is more than safety
Here's an example: `Stdin` and `File` both implement the `Read` trait. Therefore, you can write a function that takes the traits that you want to use as input parameters.

fn do_something_with<S: Read>(source: S) {

    // do something with input source
}

Then you can do the following:

let stdout = io::stdout();

do_something_with(stdout.lock());

let file = File::open(path).unwrap();

do_something_with(file);

You're free to narrow down/expand the types/methods that can be used with the function if you add more traits.

mmstick··on Rust is more than safety
We've not had any issues developing the Ion shell in Rust -- performs as well as Dash. Basically, you can't come at Rust with the mindset of object-oriented programming. Make your shell event-driven and you'll do perfectly well. Queue your signals and act on them at a later time when you can.

I've been particularly fond of Rust's Iterator trait which has been quite valuable for efficient parsing in Ion.

mmstick··on Rust is more than safety
That depends on the allocator you are using. Rust bundles jemalloc by default, which organizes deallocations/allocations such that they are very efficient.
mmstick··on Rust is more than safety
The `?` operator is just syntax sugar for:

let value = match attempt_something() {

   Ok(value) => value,

   Err(error) => return SomeErr::Kind(error),
}

That way you only need to type

let value = do_something().map_err(SomeErr::Kind)?;

It was called for by the community because error checking is something that can happen multiple times within the same function. Simply passing the error up the chain and handling all your errors in a central location is highly convenient, especially when matching requires that you handle all cases.

Additionally, in no way is C++ more efficient at error handling that Rust. Rust is able to perform this with zero allocation costs.

mmstick··on Rust is more than safety
Exceptions have high cost so I don't see where you are coming from. Rust's RAII goes further along than C++'s. C++ is playing a game of catchup to steal the move semantics from Rust.
mmstick··on Rust is more than safety
Responses are on the official Rust reddit forum.

https://www.reddit.com/r/rust/comments/5krhr0/rust_is_more_t...

mmstick··on Rust is more than safety
This what it would actually look like:

https://gist.github.com/mmstick/797fbc1cf20c92f05d33a85c38e1...

mmstick··on Rust is more than safety
That's not what standard Rust source code looks like. I don't see the point in the &* or boxing values. It's not very idiomatic.

This is more representative of actual Rust software practices:

https://gist.github.com/mmstick/797fbc1cf20c92f05d33a85c38e1...

mmstick··on Rust is more than safety
This only applies if you are using system libraries. Most libraries do not rely on external dependencies. Regardless, cargo is build tool for Rust, not a replacement for your distribution's package manager. If you want to build a library that's fully static then use the musl target and build your external dependencies with musl. If you want distribution integration then use a makefile or other tool.
mmstick··on Rust is more than safety
Rust is very expressive. The main benefits of functional programming languages is contained with Rust's Iterator trait and the ad-hoc polymorphism that traits allow.
mmstick··on Rust is more than safety
Rust's allocation strategy beats the JVM hands down in every benchmark. 20+ years of engineering cannot cover the flaws of VM and GC languages. Writing efficient stack-allocated software will always trump GC/VM solutions.
mmstick··on Rust is more than safety
I typically explain that Rust's safety has side effects that are beneficial outside the obviousness of merely being safe. Namely, Rust's safety allows for migrating runtime checks into compile-time checks when constructing state machines around the ownership mechanism. Additionally, Rust's safety allows for additional compiler optimizations, because that safety can make more guarantees about the safeness of certain internal compiler flags. These have a speed benefit in addition to the already existing convenience benefits of not having to fight with broken code. I'd much rather fight a borrow checker than to fight through gdb's and lldb's to figure out why everything's totally broken.

Then I go on to explain that there's a lot of great features in Rust that make it more convenient than C/C++ in a wide range of general purpose programming tasks. The pattern matching, sum types (Option/Result), error handling strategy, and my favorite, the Iterator trait.

For the performance-minded C/C++ software developers, I explain that the safety that Rust provides via lifetimes and ownership allows me to comfortably perform extreme performance optimizations that would otherwise be incredibly nasty and error prone in C/C++ codebases. I can feel safe knowing that if it compiles, it's almost always going to work how I expect.

Then there's the whole test-driven development mentality to Rust software development that Rust provides with `cargo test` and `cargo bench`, and data-oriented programming approach that Rust takes versus the inferior object-oriented approach via protocol-oriented programming (traits) to feature ad-hoc polymorphism and generics. It's a bit more complicated to explain that though.

Then there's the whole community/tooling/documentation pitch. I think this is one of the more important aspects of Rust as it demonstrates that Rust has a future and is more than just a niche. It has momentum which cannot be destroyed. It's a language invented during the age of the Internet and takes full advantage of that.

Rust does not employ guerrilla warfare tactics like other languages have done in the past. You can think of C/C++/Java/etc. as long-running empires. Guerrilla warfare-style tactics simply don't work against them. Emerging languages have always targeted C/C++/Java/etc. through guerrilla warfare tactics -- a loose community that mostly does their own thing. Rust is ambitiously establishing an empire of it's own through it's community -- a plethora of official projects and services: rustup, cargo, crates.io, docs.rs, users forum, internals forum, reddit thread, official documentation, docs team, an official book, GitHub hosting, etc. It's a recipe for success.

mmstick··on Rust vs C Pitfalls
Rust's default hashmap algorithm is DoS-immune for any kind of input, which is a perfectly logical default algorithm for a language intended to be used in security-critical areas like operating systems, system libraries, web browsers, etc. and promotes memory safety.
mmstick··on Rust vs C Pitfalls
> The fact that someone says on HN "we're backwards compatible" doesn't quite offer the same level of trust as an ISO standard.

Then you shouldn't take the word of a random person but from the Semantic Versioning guidelines. If you're not following the semantic versioning guidelines then you're doing it wrong.

http://semver.org/

> Especially when there are some conflicting reports out there i.e. there are many super-awesome libraries that only work with nightly (I think it's called), because many of those maintainers are early adopters that love to try out new features.

Totally irrelevant. Very few libraries, if any, require a nightly compiler. The libraries that do offer features that require a nightly compiler have those features as optional. Anyway, once the new Macros update releases, the need for that nightly compiler will go away. These libraries are just taking advantage of compiler plugins.

mmstick··on Rust vs C Pitfalls
> C does have a standard library. It's described in full in the ANSI/ISO/IEC standard for the C programming language, and it's on your system, likely called libc or something along those lines.

What you're describing is the spec and not the implementation. An implementation would be glibc (GNU Libc with open source proprietary extensions) and musl (MIT implementation that strictly adheres to spec). These libraries are updated often.

Meanwhile, Rust offers more than just a specification. It offers an implementation. This is libcore-rust + libstd-rust. This is what receives updates each 6 weeks. It also offers a compiler that comes with it, rustc, which is also what receives updates each 6 weeks.

Rust strictly adheres to semantic versioning, so changes to the API are not allowed without a major version bump. Code that was written for Rust 1.0 will still compile on Rust 1.15. Code written for Rust 1.15 may not compile for a Rust 1.0 compiler though because bumping the second version implies adding new features. Rust doesn't yet utilize a patch version though. There's never been any critical issues discovered in each stable release.

> Frankly, I don't see wide adoption happening in the corporate world until there's an LTS compiler at the very least.

Can't say that I understand why a LTS compiler would be required for adoption. Many in the corporate world are already using Rust, either publicly as an official friend of Rust or privately in smaller projects. There's nothing a LTS compiler would provide that would be beneficial to the corporate world. Semantic versioning and the ease of managing Rust toolchains with rustup has everything pretty well covered.

mmstick··on Rust vs C Pitfalls
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.
mmstick··on Rust vs C Pitfalls
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.

mmstick··on Rust vs C Pitfalls
The same is true of any technology. One cannot give an informed opinion without first being informed about the technology in the first place. Technology moves forward because there are many whom are willing to leave the comfort of their boxes into the unknown to try things without being required to be convinced.
mmstick··on GNU Parallel
Should check out the documentation for my Rust implementation of Parallel. Same syntax applies.

https://github.com/mmstick/parallel

mmstick··on GNU Parallel
Developer here. Noticed significant traffic coming from this domain. Not sure if this specific comment is the cause of that though. Kind of difficult to search this site.

Anyway, I did and am continuing to develop this with Rust as one of it's biggest points. It's a cool project to showcase Rust to both users and other software developers. I can use it to teach others Rust by example with well documented code, and possibly gain some buzz for this great language.

That the application has low overhead is mainly a side effect of choosing to develop this application in a low level language like Rust, but the speed is indeed my biggest selling point in general, for now at least. I'm continuing to commit further development into mitigating costs as much as possible.

I can also point out at there is a benefit to using this over the GNU Perl application for large scale computational tasks. Tasks where you are processing hundreds of thousands to hundreds of millions of inputs. This will keep memory consumption low and reduce the amount of CPU cycles required to process those inputs. You could save anywhere from a few minutes to an hour.

mmstick··on Rust vs C Pitfalls
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.
mmstick··on Rust vs C Pitfalls
I've seen fatal flaws from supposed experienced C and C++ developers in a large number of high profile projects, that would otherwise be nuked from existence had they used Rust.

I've also encountered severe flaws in libraries written by experienced programmers working for companies like Google and Red Hat that the Rust compiler caught during translation to Rust.

That's not counting the immense amount of tooling and features that make managing large projects a breeze in Rust. Managing a 100K Rust project with a large team is easier than managing a 100K C++ project with a large team. You can't guarantee that no one will ever make mistakes.

I find segmentation faults to be common place in C/C++ projects from experienced developers, and it's quite difficult to debug them at times.

mmstick··on Rust vs C Pitfalls
DICE and others have been investigating and using it to create tools for developing games. There is interest in using it within game engines, but there's just the issue of Rust support for major consoles. There is great interest in the PC gaming world though that's not constrained by console support.
mmstick··on Rust vs C Pitfalls
> I would prefer a str be a str be a str, regardless of how you got it. Lowercase type-name and fundamental like an integer.

The lowercase type names are reserved to primitives. The String type is not a primitive but a comprehensive data structure, hence the capital S. The String type contains an `str` primitive though, along with size information.

> I don't want to become a string expert to build a filename, and the default implementation could (at least conceptually) be always on the heap for all I care.

Is it that hard to understand that when you create a string, you will create it as either a `String` or `PathBuf`? File methods are designed to automatically convert input parameters into a `&Path` so it doesn't matter what string structure you provide.

There is also no way (currently) to create a stack-allocated string with the standard library out of the box. You can do this with crates like `arrayvec` though. It's very much opt-in for that performance.

let path = String::from("/tmp/file");

let mut file = File::open(&path).unwrap();

> Looking at the present and the future, why is that a sensible default? Both x64 and ARM are going to use a 64 bit integer register for the operations, and many of those operations are going to be 1-clock throughput. You can probably find a counter example, but 32 bit integers aren't generally faster than 64 bit ones.

No need to use a 64-bit integer when you only need a 32-bit integer. You can fit two 32-bit integers into a single 64-bit integer and perform a calculation on both simultaneously with a single cycle, versus spending two cycles to calculate two 64-bit integers. There's also no need to pay that memory cost either.

mmstick··on Rust vs C Pitfalls
> 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 no point in it. It's tucked away inside the `String` that created it.

Meanwhile, an `&str` is a slice of that fixed-size `str` array that's hidden within the `String`. You can think of a `str` as an `[char]` with it's own special `str` methods, and an `&str` as an `&[char]` which has access to all of the `str` methods as well.

> Speaking of integers, Rust's choice of 32 bit integers as the default for literals is painful given that every machine most of us will ever care about is now 64 bit. I routinely deal with arrays and files that are too large for a 32 bit integer.

I'm pretty sure the default integer is a `usize`, which is 32-bit on 32-bit systems and 64-bit on 64-bit systems.

mmstick··on Rust vs C Pitfalls
Is there a problem with doing that with unsafe code? You have to remember that unsafe doesn't mean unsafe in Rust. If a developer is choosing to use the unsafe keyword, it's merely telling the compiler that they know what they are doing and what they are doing is safe. One shouldn't automatically infer that unsafe code is bad or negative. It doesn't deserve the negative stigmatism that it's given.
← PreviousPage 4 of 5Next →