18
Comments
  • 10
    🫡
  • 15
    What part of Option<Arc<Vec<Option<dyn Foobar + Send>>>> did you not understand
  • 10
    @12bitfloat add three more nesting levels to unlock “Git Conflict Markers Are Now Valid Rust” achievement
  • 3
    C++ accommodates zero-cost better. Rust is a lot easier if you ppermit sacrificing some pperformance in the interest of convenience, for example by returning trait objects when you could technically spell out a ridiculous, 10 lines long concrete type.
  • 4
    Then again, C++ doesn't really have third party impls so interoppperability always must hhappen through an additional object. I honestly don't know enough about it to determine whether this is a problem.
  • 5
    @lorentz I mean... you could also just use a type alias

    C++ and Rust are really similar when it comes to generated machine code. Both are suboptiomal for a lot of very specific reasons

    In Rust for example a for loop over an inclusive range like 1..=10 can be quite a bit slower since it has to guard against overflow

    In C++ you can get bad codegen if you use std::move where you shouldn't because I think that prevents named return value optimization

    Fun!
  • 3
    @lorentz

    C++ does have "third party" implementations, in the sense that the committee only defines the language, it doesn't implement anything. And oh boy shit will happen if you try and mix different g++, msvc++ and clang.

    However I think the "competition" results in better implementations.

    @12bitfloat

    Compilers nowadays are smart enough to prevent those footguns, since it's easy to statically determine when something acts as a rvalue, and will always perform RVO when possible even if you do retarded shit.

    std::move is just a lexicographically fancy static_cast<T&&>(), which doesn't really have any implications for auto variables.

    The problem, as always, is people wanting to write fancy code and not knowing what they are doing.

    Cppreference explicitly states that manually using std::move is, 99% of the time, a mistake, and hence compilers try to protect you from it.
  • 1
    @CoreFusionX hmm, I used it once quite long ago, don't remember the use case. But not a c++ expert in general. Not a big fan.
  • 1
    Also, while not really being an example of zero cost abstractions, I love that c++'s *runtime* safety is opt-in.

    Shit like iterator/range checking is done by pretty much all major c++ runtimes... When using their debug version.

    Not a fan of paying for that shit in release mode.
  • 1
    @retoor

    The use case is really making sure you get a rvalue reference to whatever you will be requesting to move (really std::move should have been called std::rvalue_ref_cast), because you intend to pass it to (possibly external) other code that might optimize based on copy or move semantics.

    However, except very few and far corner cases, good library implementors will either

    - explicitly delete copy or move semantics where they don't make sense, resulting in an error if you try to use them.

    - just take a const lvalue reference, and let compiler do its magic (99% of the time better than what you intend to do)

    - do perfect forwarding if it's just forwarding to other code, delegating to problem to the callee.

    Only in the second case, if for whatever reason a const T& would violate const correctness and you'd need to take a T& or T&& explicitly, would a manual std::move make sense.

    I have seen it only once in app (as in, not library) code.
  • 1
    @CoreFusionX I really needed it because it didn't work some other way iirc. It wasn't to be fancy or optimaztion. Really needed.
  • 1
    @12bitfloat That's really interesting, I assumed that if it's really that expensive they would just check the max value before entering the loop.
  • 1
    Especially with iterators being such well-defined distinct values with lifetimes and immutability guarantees, such an optimization doesn't sound very difficult. But maybe I'm oversimplifying.
  • 1
    @CoreFusionX The compiler can't always prevent stuff like that since sometimes it's in the semantics of the program

    E.g. if you return a string as const, the compiler HAS to copy it, it can't move from it
  • 0
    @lorentz LLVM apparently sucks at optimizing it and Rust hasn't put any effort in optimizing it at the IR stage

    But yeah, it really shouldn't be too difficult
  • 1
    @12bitfloat

    It has to copy if you create a const string inside the function body and then you return it.

    Having const as return type and returning an auto non const object won't invalidate RVO.

    Problem is, again, people not understanding the mechanism and just sprinkling const everywhere because they read it in some trend blog, just like sprinkling std::move around.
  • 0
    @CoreFusionX Sure, but that's what I'm saying. Both Rust's and C++'s zero cost abstractions aren't always zero cost if you don't know how to use them properly
  • 0
    @12bitfloat

    Thing is, RVO and move semantics are zero cost abstractions in that sense.

    The whole point of a zero cost abstraction is don't pay for it if you ain't using it.

    That's why forced bounds checking, and similar runtime stuff is not a zero cost abstraction (you pay for it even if you don't need it).

    move semantics, if you use them explicitly, and wrongly, will cost you performance, but they don't otherwise impose a runtime cost if not used, or if you let the compiler do its magic.
  • 1
    @CoreFusionX Depends on the definition

    I know zero cost abstractions as "you couldn't write it any better by hand"

    In that sense bounds checking are zero cost abstractions if that's what you wanted, and it's pretty much always what you want. Seriously, branch predictors are really good so bounds checks cost like 1% of performance even in the worst case which is very array heavy code

    Do note that Rust doesn't force bounds checks, you are free to use the unsafe methods if you know what you're doing.... probably not a good idea most of the time though
  • 1
    Ok, I‘m triggered 😂

    Wtf is that supposed to mean? That zero cost abstractions don’t exist?

    Is this a word that is too scary for JS devs? 😄
  • 1
    @12bitfloat

    Well, that's not really the definition, at least the way the c++ committee enforces them.

    By their own admission, it means that anything that gets added to the language (and everything at this point is abstractions really) must not impose runtime penalties on compatible code from previous versions if you are not *using* said abstractions, without need to configure or change anything in the code.
  • 0
    @CoreFusionX okay, I need to find out what they said then. I was under the impression it wasn't a cost in memory footprint. So a class with the same variables as a struct would not incur additional memory cost. That is why "this" is just a pointer. I have my c++ book I should look at it I guess.
  • 2
    @Demolishun

    "struct" in C++ is just syntactic sugar for "public class" with default public accessibility. There's no functional difference, and of course, no runtime penalty either from

    struct A { int i; };

    To

    public class A {
    public:
    int i;
    };

    They are the same and compile to the same.

    Zero cost abstractions are what they are.

    For example, smart pointers. An abstraction for reference counted pointers, which could be used for say, garbage collection.

    In C#, java, or JS, you pay for this always. Every reference is reference counted and thus you pay the price, even if seemingly negligible.

    C++ doesn't impose this penalty. You have smart pointers. You don't want to use them? Fine. They will cost you no runtime performance.

    Yes, you will be on your own and might have memory leaks, but believe me, when you have to deal with very hot loops with allocations, those negligible penalties, will amount to non negligible penalties.
  • 1
    And that is why, despite other languages doing a good job so far, everything performance critical will still be done in C/C++.
  • 1
    @CoreFusionX well, runtime garbage collection is not the only automatic memory management system.
    Swift has automatic reference counting that is performed at compile time. It has zero cost because it‘s literally what you manually would do, but done by the compiler.
    I think Rust does the same.
  • 1
    Yay, C++ porn in the comments! 😙
Add Comment