Logan Smith

Logan Smith

I'm Logan. I love computer programming and learning new stuff. Come hang out and geek out with me.

Cursed C++ Casts

Cursed C++ Casts

Choose the Right Option

Choose the Right Option

Use Arc Instead of Vec

Use Arc Instead of Vec

Пікірлер

  • @drew-et1mm
    @drew-et1mm2 сағат бұрын

    I watch this video once a month

  • @atesztoth
    @atesztoth10 сағат бұрын

    … and you just single handedly convinced me to like & subscribe with the bell turned to the max without even asking. Is it a (TotalSubscriber*) randomViewer? 🤔🤔🤔

  • @T1Oracle
    @T1OracleКүн бұрын

    You don't need Rust in C++, you just need to use Rust.

  • @IllidanS4
    @IllidanS4Күн бұрын

    Sounds like a good rule of thumb would be this: could you replace each AddPoint with AddPoints on a 1-sized array without a performance drop (irrespective of the heap allocation)?

  • @IllidanS4
    @IllidanS4Күн бұрын

    I don't really think that the performance issue in C++ has to do with the fact that functions are "structurally typed". Sure, having a unique type for each function would simplify the optimization, but I'd fully expect the compiler here to acknowledge that you are passing the same function each time. It does so for normal values, so why not functions? Regardless, it is relatively uncomplicated to fix this without changing too much of your code, here's just quick code that I wrote that should help: template <auto Func> constexpr const inline auto monomorphic = [](auto&&... args) { return Func(std::forward<decltype(args)>(args)...); }; Using monomorphic<f> instead of f gives you the benefit of the lambda type, while not sacrificing much of readability or conventions.

  • @IllidanS4
    @IllidanS4Күн бұрын

    Pedantic complaint about the compiler's wording ‒ "no two closures, even if identical, have the same type" is technically incorrect. In the following code: let mut local = |x: i32| x; let local2 = local; local = local2; assert!(local.type_id() == local2.type_id()); The variables "local" and "local2" are our two closures, obviously "identical" (meaning "one and the same") since they come from the same definition, yet one can be assigned to the another, and their types are equal. It would be better to say something like "the type of a closure is unique and is not equal to the type of any other closure, even with equivalent definition".

  • @correabuscar
    @correabuscar5 күн бұрын

    this video makes me glad that I'm not smart enough to know C++

  • @kirillandrianov953
    @kirillandrianov9535 күн бұрын

    why have you marked the c++ function no-inline and then complained about the function not inlined(?) makes no sense to me basically, this example you wrote is perfectly inlinable and the generated code should be almost the same as the rust one upd: I found the explanation comment

  • @xmorse
    @xmorse6 күн бұрын

    I would use ArrayString stored on the stack so you have copy trait and can forget about ownership

  • @gaeel330
    @gaeel3306 күн бұрын

    I really enjoyed this video, I've used a bunch of languages with constructors (C++, Java, C#) and have been using Rust for a little while now. The other day, I had a little C# to write, and when working with constructors I felt a familiar tiny little pang of dread again, and I think you just nailed down what I was feeling. The metaphor that a constructor is a function that returns an initialised object isn't quite true, and it's in the details where some subtle ambiguities and bugs often lie. Rust applies the metaphor much more literally, and I feel a lot more confident in what I'm doing. Funnily enough, this is one of the things I've appreciated the most with Rust. There are much fewer "things" to consider in the language. It's closer to C in many ways in that regard.

  • @JoshBlasy
    @JoshBlasy8 күн бұрын

    One of the biggest learning curves I've experienced, going from languages like Python on JS to rust, is forcing myself to trust the compiler. I don't have to do anything convoluted, I just write code and the compiler does its job. This video is a good example of that

  • @Lircking
    @Lircking10 күн бұрын

    epic video

  • @SamualN
    @SamualN10 күн бұрын

    5:09 this breaks my brain

  • @nickdarker3832
    @nickdarker383211 күн бұрын

    Fantastic video explaining the algorithm and it's implementation!

  • @TheJaguar1983
    @TheJaguar198314 күн бұрын

    Talking about Rust and C++ in the same video without bagging one out as superior to the other? Inconceivable! 😂

  • @vechnonedovolen666
    @vechnonedovolen66616 күн бұрын

    11:39 what "int C::*" mean ?

  • @TekExplorer
    @TekExplorer16 күн бұрын

    What the hell. I am never using c++. Not fking worth it.

  • @vk8a8
    @vk8a818 күн бұрын

    I want Joe Biden to be youtuber

  • @lobovutare
    @lobovutare19 күн бұрын

    Just a small tip: you started talking about monomorphization as if everybody knows what that means. I had to look it up.

  • @Murukku47
    @Murukku4721 күн бұрын

    Somehow C-style 'type(value)' casts always felt most aesthetic to me since it just looks like a function you give some other data type as an argument that then turns into the data type the function is named after.

  • @brockdaniel8845
    @brockdaniel884521 күн бұрын

    Maybe using consteval ?

  • @maxbd2618
    @maxbd261821 күн бұрын

    I don't do Rust but I like these great quality videos, however I'm a little critical on the part about returning direct references and applying that to other languages like C++. In C++ that probably isn't the best idea because you can end up with UAF bugs because a lot of times (especially returning views from functions) the view will live longer than the actual object that allocated the memory. Is that not a problem in Rust because of the borrow checker?

  • @phillipsusi1791
    @phillipsusi179121 күн бұрын

    I'll go one further... monster name strings likely are static anyhow, and so you don't even need Arc<str> or Box<str>, you can just use str directly. Then clones don't even need to increment a reference count on the heap, you just copy the pointer.

  • @dagoberttrump9290
    @dagoberttrump929028 күн бұрын

    Coming from c++ newly to rust, a few things don't make sense to me in this explanation. maybe someone can explain: - since we're talking about immutable vecs and string slices, why do we care for thread safety? if it can't be changed from either thread, we might as well just use Rc - even using just rc doesn't really make sense to me. after all, vectors and strings are already smartpointers. so wrapping them in another smartpointer just creates an unnecessary indirection. the clone should still be O(1) since if i understand correctly only stack metadata is copied, and copying 24 bytes instead of 16 really shouldn't make a big difference on modern cpu's - string slices in rust are probably implemented the same way as in c++, which would be ptr + size or a total of 16 bytes on x64. the compiler usually passes such small structs in registers, meaning passing a str directly without wrapping it in a smartpointer will always be faster. that's the whole point of slices actually. since the data likely resides in the rodata section of the binary, its lifetime is static furthermore, so it should always be valid.

  • @_noisecode
    @_noisecode27 күн бұрын

    Hey, thanks for the thoughtful comment! Here are my quick responses to your bullets: - You're right that the pointed-to data is safe from race conditions, since it's immutable--but the reference count itself still needs to be thread safe if the shared ownership is cloned around on multiple threads simultaneously. Rust provides Arc for this case, and Rc for when you don't need the overhead. - I might recommend you watch the video again I guess. One of the key points I made was that there actually ISN'T a double indirection when using Arc<str> (like there would be with Arc<String>)--the 'str' part means the Arc owns the str buffer directly. Also, cloning a String or Vec is not O(1)--String and Vec have unique ownership, so cloning is 'deep' and expensive. - Static strings are of course going to be simpler and probably faster, when they're an option. This video discusses an optimization for the case when you don't know the strings ahead of time.

  • @NilEoe
    @NilEoeАй бұрын

    Yes please, more interesting Rust content

  • @torsten_dev
    @torsten_devАй бұрын

    I'd prefer a Cow<str>.

  • @meyou118
    @meyou118Ай бұрын

    ty - i like rust more and more once things are explain ed like this

  • @khronos142
    @khronos142Ай бұрын

    nice work

  • @Gennys
    @GennysАй бұрын

    It's been very hard for me to decouple a lot of these very atomistic subjects from my original understanding of them coming from Java. All of these disparate concepts being baked in and "solved" with Java's particular inheritance model. The entire time I'm thinking to myself "Rust doesn't have simple interfaces?" xD I'm learning though, every day.

  • @woosix7735
    @woosix7735Ай бұрын

    Interesting interesting

  • @FishMan1nsk
    @FishMan1nskАй бұрын

    Yo! This one is pretty usefull and there are not a lot of usefull geometry script videos on you tube. So thanks.

  • @norude
    @norudeАй бұрын

    Do calls to NonZeroU8::new just get optimized out, because in memory it returns the same thing as it receives? If so, why would NonZeroU8::new_unchecked be necessary

  • @martingeorgiev999
    @martingeorgiev999Ай бұрын

    Under the hood the return values of both factories have the same memory layout but the compiler differentiates their types. i.e. when you call NonZeroU8::new() you get an Option which you have to handle or else you wouldn't be able to use the NonZeroU8 at all. In code: let x = NonZeroU8::new_unchecked(0); print!("{}", 1 / x.get()); causes a panic(crashes at runtime) In contrast: let x = NonZeroU8::new(0); print!("{}", 1 / x.get()); wouldn't compile because 'x' has type Option and you can't divide an integer by Option. You would have to do something like: let x = NonZeroU8::new(0); match x { Some(valid) => print!("{}", 1 / valid.get()); None => {} // there really is no NonZeroU8 to divide by }

  • @norude
    @norudeАй бұрын

    What if there's more than one failure?

  • @npip99
    @npip99Ай бұрын

    So basically, all of the C programmers that use "null" have just been justified . But regardless, that's actually correct. Ideally, you want Rust to compile identical to C, but simply give you type-safety around your otherwise unsafe C code. C's NULL acts like an Optional<&T>, so you should use the same type in Rust.

  • @DeVibe.
    @DeVibe.Ай бұрын

    Just use C++ 🤷‍♂️

  • @ultimatedude5686
    @ultimatedude5686Ай бұрын

    I would argue that if you are exposing the function data_mut(&mut self) -> &mut Option<Data>, and you want to provide an immutable accessor, you should use data(&self) -> &Option<Data> for consistency. Though data_mut would be a somewhat strange method, and if you're already breaking encapsulation to that point you may as well just make your data field public.

  • @elin4364
    @elin4364Ай бұрын

    for the monster id example, why not store a &'static str instead?

  • @kavehtehrani
    @kavehtehraniАй бұрын

    Your videos are extremely high quality! Please make more! I'd happily to pay for an in depth course.

  • @programming5274
    @programming5274Ай бұрын

    17:17 To optimize this, double box the Err variant. size_of::<Result<(), Box<Box<dyn Error>>>>() == 8. You don't use the extra heap allocation unless you return an error. type DoubleBox<T> = Box<Box<T>>; println!("{:?}", size_of::<Result<(), DoubleBox<dyn Error>>>());

  • @woosix7735
    @woosix7735Ай бұрын

    Wow, that memory layout thing kinda reminds me of of NULL pointers in C execpt abstracted away such that you don't have to thing about null pointers! great!

  • @Kitulous
    @KitulousАй бұрын

    why do we need public_cast if we can do this with type punning? #include <iostream> #include <bit> class WithPrivate { private: int x = 123; public: void PrintX() { std::cout << x << std::endl; } }; class WithPublic { public: int x = 123; }; int main() { WithPrivate wpriv; wpriv.PrintX(); WithPublic* wpubptr; wpubptr = (WithPublic*)&wpriv; wpubptr->x = 456; wpriv.PrintX(); std::cout << &wpriv << std::endl; std::cout << wpubptr << std::endl; return 0; } prints 123 and 456 or is UB the problem?

  • @microcolonel
    @microcolonelАй бұрын

    I love how the best solution to C++'s unergonomic initialization is to make a C struct. 😂

  • @CodeVault
    @CodeVault2 ай бұрын

    Every time I watch a video of yours I feel there's still so much left to learn about Rust. The quality of your videos and the explanations you're giving really help others like me understand the nuances of Rust. Thanks for the video. Keep up the good the good work!

  • @BobChess
    @BobChess2 ай бұрын

    Wtf is reserve? I only use Scratch

  • @tmlen845
    @tmlen8452 ай бұрын

    The same could have been said in a few seconds. Of course the purpose of reserve() is to avoid doing many reallocations, and it will be counterproductive if you call it repeatedly while adding elements...

  • @hampus23
    @hampus2315 күн бұрын

    💯

  • @TCSyndicate
    @TCSyndicate2 ай бұрын

    Commenters have pointed it out somewhat, but this video represents a misunderstanding of the purpose of different types. What you want here is a &[T] not an Arc<[T]>. The confusion is sometimes you feel forced to make an allocation, cause you're doing something like giving monsters ids from a loaded configuration file. In that case, you make 1 allocation at the start of the program for the config file, then each monster holds a &str to that allocation. Having to make an allocation for the config file, doesn't mean you need to make, or hold an allocation for each thing that uses the config file. Consider writing a programming language implementation, with multiple parsing phases. The efficient thing to do is to make 1 String allocation at the start of the program for the source code, then a lex(&str) -> Vec<&str>, containing subslices of the original String buffer.

  • @CYBERNETIC-ANGEL
    @CYBERNETIC-ANGEL2 ай бұрын

    concise, easy to understand and straight to the point, you earned yourself a sub

  • @eli1882
    @eli18822 ай бұрын

    Who the fuck calls std "stood"?

  • @dragenn
    @dragenn2 ай бұрын

    Randomly came across this video and enjoy going over some fundamentals.

  • @robert36902
    @robert369022 ай бұрын

    Nice explanation, especially how we can avoid the problem by calling the right function on the vector that adds all elements at once!