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

I was at the sidelines when Rust 1.0 was being made and I think it got into an llvm induced feedback loop. Slowly turning into C or C++ with other features but the same type, object and memory model.

Part of the reason was Rust's desire to show itself as a direct competitor w.r.t performance, I think.



Performance, but with sanity.

Say you use a vector in C++. You push one element and pop two. In Rust that's a None. In C++ the answer is UB (in my case 43).


In C++ it is an error, if one bothers to enable bounds checking.

https://godbolt.org/z/vYcMhE9h7

> Error: attempt to access an element in an empty container.


Someone much more insightful than me pointed out that most of the safety advantages of Rust are really a cultural phenomenon, rather than a strictly technical one. You could write unsafe unsafe Rust that derefs invalid pointers all day but when building systems and libraries with Rust, people value safety and Rust enables that as a priority.


Yes, that is kind of true.

It is also what attracted me into C++ coming from Turbo Pascal and Turbo Basic, back in the early 1990's.

Although C++ culture could be much better towards safety, it is definitely better than whatever WG14 is doing, or C has brought into the picture for the last 50 years.

Also anyone that just copy pastes C like code into C++, is the kind of developer that will be using unsafe{} all over the place, on the languages that have them.


Telling people to stop using unsafe is much easier than telling people to not have undefined behaviour.

C developers like telling themselves that only people with bounded rationality make security critical mistakes. All the skilled C developers have ascended beyond the mortal realm and would never let themselves be chained up with crutches for the weak like affine types or overflow/bounds checking.


> Someone much more insightful than me pointed out that most of the safety advantages of Rust are really a cultural phenomenon, rather than a strictly technical one

It's not. It's a safety phenomenon. See Java, C# etc.

Java doesn't have huge focus on it, but managed to get it right.


> a cultural phenomenon, rather than a strictly technical one. You could write unsafe unsafe Rust that derefs invalid pointers all day

Yes you could. But I do not think " cultural" is the right term for not putting all your code in an `unsafe` block


It's not an error by default and more importantly it's not an error under right circumstances (e.g. lets say number of pops and inserts is supplied via arguments).


Yes defaults matter, it doesn't change the fact that protective gear is available, and like seatbelts, helmets and motorrad full body armour, it is up for security conscious people to make do of what is made available to them.

As for your dynamic arguments request, have fun,

https://godbolt.org/z/YYKrnh4Y9


It's not just that defaults matter, the problem here is why have footgun as the default?

In a runtime example I can run it with tests and it would behave fine if both values are same or first arg is bigger, in Rust's case it would behave valid for ANY combination of arguments.


Likewise with C++ if the safety belt is buckled.


Heavy disagree. This is less giving a safety belt and more like saying we got airbags (that are deadly without seatbelts) and a DIY seat belt is somewhere in there too (it's in a box under the seat).

Coming from Java the C++ stuff is jarringly unsafe.


Agree that Java is much better than using raw C++.

Still, if you want to contribute to OpenJDK internals, they will only be taking pull requests in C++, not Rust.

So better learn how to build those seatbelts and airbags, for all the projects we rely on, including Rust's own reference implementation, that aren't going to rewrite their code from C++ into something else.


And what should be defined behavior? There are quite a few choices that depend on particular situations. Feature designers have no knowledge about what would you want so they left it up to application programmer to check the situation upfront and act accordingly to their wishes. If you want same behavior across the whole application you can always write generic function doing just that.


Return either value or an EMPTY placeholder. Look Java did it. It returns a nullable value or Optional.


Then I have to check the result anyways. Same thing


No. In Rust check is there by default, with optional unchecked access. In C++ the safety is off by default and you have to remember to check.

It's like Yaml parsers, they had "load_yaml" which is unsafe and "safe_load_yaml" which is the secure option. Imagine no surprise when Rubyists went for shorter safe looking method and got their servers pwned.


I think you are wrong. I looked and in Rust doc it says that: "Removes the last element from a vector and returns it, or None if it is empty". You better be doing check for "None".


The check is still there, that's not under dispute. GP is saying that it's still not the "same thing" as C++, since Rust will refuse to compile the code unless you either perform the check, or actively opt into unchecked access. In contrast, C++ will happily compile the code if you unintentionally neglect to perform the check.


Are you saying that Rust will refuse to compile code that does not explicitly check option return result for empty?


To be precise, Rust will refuse to compile code that accesses the value without checking that it is not empty.

Rust does that with the combination of "sum types" (aka "enums with data") and pattern matching. An `Option<T>` is either `Some(T)` or `None`. Matching on an option with the pattern `Some(value)` creates a new syntactic scope where the value is accessible. This scope is only entered if the `Option` is actually non-empty.

All ways of getting the value from the option are ultimately shorthand for a match in that way. For example, option.unwrap() will either get the value if it there, or panic. Option.unwrap_or(x) will either get the value of it there, or use x instead.

In practice this is x100 less error prone than C++. Source: was burned by vector.front() on empty vectors more than once. In C++, UB. In rust, usually the "empty case" is considered when first writing the code, or at least caught during review (unwraps tend to be very visible in reviews)


>Are you saying that Rust will refuse to compile code that does not explicitly

Depends what you mean by explicit.

Look at it like this:

    pub fn main() { 
      let mut vec : Vec<i32> = vec![];
      let x = vec.pop();
      println!("{:?}",x); // prints: None
    }
In this case you see the value is missing.

    vec.pop().expect("I want a value") 
will panic with "I want a value" because value is empty. And most rigorous way to deal with it is:

    if let Some(x) = vec.pop() {
       println!("{:?}",x); 
    } else {
       println!("EMPTY"); // prints: Empty
    }
will either print value or EMPTY depending on what the vector contains.

Types like Result will issue warning that you didn't handle the cases.


for some reason I can not answer your subsequent reply so I do it here:

I looked at your example and played a bit with it and yes I agree with you - compiler does help in this case.

My old text:

So instead of checking if vector is not empty you check that the return result is not empty. I do not see much difference. If Rust compiler would choke when "else" clause in your example is not present I would understand your point about compiler preventing improper access.


`x`, the value of the Vector access, only exists within the context of the first block. It does not exist in any other scope. This makes it impossible to access when the result is not valid.

> If Rust compiler would choke when "else" clause in your example is not present

The compiler won't choke, but it will stop you from accessing the value.

It doesn't matter if you omit the `else` clause or not, the type system ensures that you can't access invalid values.

Here's a bit of an example based off of @Ygg2's code: https://play.rust-lang.org/?version=stable&mode=debug&editio...


> for some reason I can not answer your subsequent reply so I do it here

Hacker News will try and discourage really quick back and forth comments. To do so, the reply button is hidden. However, if you click on the timestamp, to go to the comment's own page, you can reply there.


Thanks for the tip. Did not know it


Yes, you cannot use the result without matching on the result/option type.


Problem is, you call pop on empty vector in C++, you get nasal demons. Not a case in Rust.

I'm not a C++ expert but here is my understanding. Vector is essentially a tuple of (dynamic_array_address: ptr, size: size_t, capacity: size_t). To pop a value from vector you just decrements size. So what happens when you have empty vec? Your size is 0, and you're substracing 1, which causes undefined behavior.

Correct way is to check BEFORE you pop_back().

In Rust, any number of invocation of pop() will not result in undefined behavior. You can ignore the value, or you can check it, but it doesn't expose you to UB just for slightly misusing a vector.


>"Correct way is to check BEFORE you pop_back()"

Absolutely and this is exactly what I do. I always check the containers before removing elements.

>"In Rust, any number of invocation of pop() will not result in undefined behavior. You can ignore the value, or you can check it, but it doesn't expose you to UB just for slightly misusing a vector."

I do not understand "slightly misusing" part. I assume in Rust it would bomb if pop() returns "none" and you try for example add said "none" to some number.


Vec<T>::pop() and similar methods return an Option<T>, so it's a compile-time type error to just add a number to it directly.


> I assume in Rust it would bomb if pop()

That's the thing. It wouldn't. Worse you can do is make whole program panic, with well defined stack trace and message.

It will never return non-sense and pretend all is ok.


You should always check

The argument is that Rust forces you and in C++ you can forget/the compiler can do what it wants


The fatal mistake here is using the STL...


The safe thing Rust did here is affordable in Rust (Option<T> is the same size as T for many T including all references) so they could afford to do it, whereas it's expensive in C++. Could it have been made cheaper in C++? Sure, but safety wasn't their priority so who cares?

That prioritisation applies to the whole ISO language, not only to the standard library.



You turned on a feature which helps diagnose this type of mistake at runtime, and it helped you by diagnosing the mistake at runtime. What does that prove?

What I'm talking about is that Rust's Option is very cheap in all the cases where it can be very cheap, which makes this whole design feature more affordable. C++ eventually grew std::optional which is not powerful enough for this work and yet is also bigger and slower. They could have done better, but safety wasn't a priority.


It proves that there is a way to diagnose this type of mistake, that is what matters.

Unlike C, which the only way to be safe is not to touch it at all.

As for performance, ISO doesn't implement compilers, there are many ways to improve performance while keeping the semantics in line with the standard.

If the compiler vendors decide to focus elsewhere is another matter, a bit like there are languages as complex as Rust and compile faster, because that has been a concern, whereas rustc developers have their concerns elsewhere.


Still, catching errors at runtime is subpar compared to catching them at compile time.

Even your dynamic example won't tell me calling pop/push x/y times respectively will fail if x > y.

I have to trigger it somehow.


Since when does Rust support dependent typing?


The fatal mistake is not enabling bounds checking, which most STL implementations support.


-Wabsolutely-everything

I haven't written the Language of Kings in a while: does GCC -Wall truly enable all warnings yet?


It doesn't, and that would be a bad thing, because warnings aren't magic and bug-free themselves. Nor is there any reason to comply with every single warning every person in the world has come up with.

clang has a -Weverything and it isn't supported.


Obviously not. It would be an instant scandal if they tried that.




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

Search: