Crust of Rust: Smart Pointers and Interior Mutability

แชร์
ฝัง
  • เผยแพร่เมื่อ 20 พ.ย. 2024

ความคิดเห็น • 208

  • @ChronosWS
    @ChronosWS 4 ปีที่แล้ว +53

    ~30:00 I very much appreciate that writing a failing test to show concurrent access leads to inconsistent results is hard - which is exactly what makes concurrent programming in general hard, and why Rust is so nice for making concurrent code that works.

    • @Knirin
      @Knirin 2 ปีที่แล้ว +1

      The test worked but not in the way he expected it to. The allocation was occurring separately from the mutation of the UnsafeCell’s internal value. Which array pointer made it into the UnsafeCell last changed between test runs.

  • @derekdreery
    @derekdreery 4 ปีที่แล้ว +200

    These are really great, and they fill the "intermediate" gap in rust tutorials, which is very valuable!

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +28

      Thanks, I'm glad you think so!

    • @dmitrij34
      @dmitrij34 4 ปีที่แล้ว

      @@jonhoo , really enjoy the content. A little question, if you don't mind...
      I've just recently started to check out Rust, so sorry if it's a dumb question - your implementation of the Cell, wouldn't it leak memory on .Set(...)? Who is responsible for the cleanup? I doubt unsafe cell would do that. Are we effectively throwing away the raw pointer?

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +7

      Ah, no, not quite. `set` overwrites the old value through a `&mut T` (that's what the dereference of the raw pointer we get back from `UnsafeCell` produces). When you change a value through a `&mut T`, Rust automatically drops the old value for you :)

    • @dmitrij34
      @dmitrij34 4 ปีที่แล้ว +1

      @@jonhoo Interesting... From what I've heard in a lot of the Rust tutorials I've assumed so too.
      And then I've took a look at the Cell implementation of the Cell in the standard library:
      pub fn set(&self, val: T) {
      let old = self.replace(val);
      drop(old);
      }
      For some reason drop here is explicitly called.
      As I understand, the replace returns T, and it should be automatically destroyed when the scope ends, but why drop then?
      Took the code from here:
      doc.rust-lang.org/src/core/cell.rs.html#344

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +11

      That's actually for a slightly different reason - the standard library Cell has a replace method, and rather than copying the unsafe code from replace into set, they re-use replace in set. In which case they also need to deal with the return value. They don't need the explicit drop there though, that's probably just for exposition.

  • @chsblue2
    @chsblue2 3 ปีที่แล้ว +9

    A cow literally started mooing outside when you began to explain Copy On Write. Thank you for sharing your knowledge. Very helpful!

    • @cat47
      @cat47 หลายเดือนก่อน

      that's pretty ironic

  • @VivekYadav-ds8oz
    @VivekYadav-ds8oz 3 ปีที่แล้ว +5

    Those two questions about taking a &mut and storing it, and modifying directly through the NonNull ptr rather than using a Cell, are what I had in my mind too! That's why you should ask questions if you're live, you don't know who else might be thinking about that too.

  • @beastle9end499
    @beastle9end499 3 ปีที่แล้ว +14

    I recently switched from C++ to Rust and that type of content is exactly what I was looking for. Thanks very much:)

    • @rubenstukken6940
      @rubenstukken6940 3 หลายเดือนก่อน

      I started looking into switching. But this kind of terrifies me. I should probably wait till I encounter a scenario where I what to do something legit and know how to do in C++ and then look into these types.

  • @bkaankose
    @bkaankose 3 ปีที่แล้ว +11

    This is one of my best videos of your collection here. Thank you for doing advanced stuff. Even though Cell might seem like a structure that don't have a lot of use cases, it helped me to understand a lot of intermediate concurrency and threading problems in programming.

  • @sedat4842
    @sedat4842 3 ปีที่แล้ว +6

    I have been looking for some intermediate/advanced tutorials on Rust, but wasn't lucky so far. Unfortunately, most talks in conferences are introductory but hese videos are great for me to continue learning.
    Thanks a lot Jon!
    Also, I donate monthly to GiveDirectly and I recommend everyone to do so.

  • @sortof3337
    @sortof3337 ปีที่แล้ว

    The bad example with bad array consistently fails on my older machine but doesn't fail on new one. These tutorials are so vaulable and insightful. I have literally learned so much from your videos. tysm.

  • @nikis05
    @nikis05 4 ปีที่แล้ว +38

    My favourite part is: “why is this working?? Great, it failed!” :D Jokes aside, thanks for an amazing tutorial!

  • @manjunath3929
    @manjunath3929 4 ปีที่แล้ว +2

    I had spent hours to understand smart pointers but this video helped me to understand in greater depth than I could reading docs. You are really gift to rust community and people like me who are new to rust. Thanks a lot Jon

  • @Codeaholic1
    @Codeaholic1 2 ปีที่แล้ว +2

    One of the better simple explanations of PhantomData and the drop check.

  • @GenusvProgramming
    @GenusvProgramming 4 ปีที่แล้ว +20

    Thank you for this! it was really helpful. I love this Crust of Rust series, I for sure will have to watch it a couple of times.

  • @nilshaberstroh2705
    @nilshaberstroh2705 3 ปีที่แล้ว +2

    Delicious content. The way you explained these pointer types makes it actually possible to distinguish and remember them. Very very valuable video.

  • @christopher8641
    @christopher8641 ปีที่แล้ว +2

    This video has been a godsend for understanding some of these concepts. You are a valuable teacher! Thanks for these vids, they have really pushed my understanding forward by quite a bit

  • @NicholasShankland
    @NicholasShankland ปีที่แล้ว

    Trying to learning rust, coming from a javascript career, and trying to transition into things i find more interesting than daily redux meandering. Very helpful. I don't have a cs degree, so in trying to learn rust i am also trying to learn cs. So the deeper explanations are wildly more helpful than just pure 'x does y', without the 'because' I will never actually be able to write anything useful... so thanks!

    • @abdullahajibade7210
      @abdullahajibade7210 ปีที่แล้ว

      if you want to learn or you learning. there are videos for that. ppl could recommend some for you if you want. there are alsoo full tutorial as the video is centerred on smart pointer

  • @sputnick1
    @sputnick1 ปีที่แล้ว

    This has got to be the most comprehensive explanation of rust’s smart pointer types ❤

  • @rurunosep
    @rurunosep ปีที่แล้ว

    I think that the PhantomData in the Rc might not be necessary. I noticed in the PhantomData chapter of the Nomicon there's a section that says that if you implement Drop on a Vec, for example, the compiler will automatically consider that Vec owns values of type T (at least for the purposes of the drop check?) even though it actually just owns a pointer. And I imagine it works exactly the same in the case of our Rc here.

    • @jonhoo
      @jonhoo  ปีที่แล้ว +1

      There's currently a lot of debate around PhantomData and the drop check, so the discussion around there may be slightly dated. I recommend github.com/rust-lang/rust/pull/103413 for the latest discussion.

  • @monawoka97
    @monawoka97 7 หลายเดือนก่อน

    These are seriously university quality lectures. I cannot tell you how much I appreciate you making all these!

  • @kevincarvalhodejesus4473
    @kevincarvalhodejesus4473 7 หลายเดือนก่อน

    That's the best rust channel on youtube. I really love such deep and explanatory videos. I been watching these series and now i feel like rust is starting to click on my mind. Thank you so much Jon for such a great content!

  • @ChristopherBreeden85
    @ChristopherBreeden85 4 ปีที่แล้ว +69

    I enjoy how Jon keeps trying to write incorrect code to show why it's incorrect but is continually stopped by the compiler :)

    • @troglodytto
      @troglodytto ปีที่แล้ว

      Rust compiler is beautiful in that sense. Literally forces you to write correct code.

  • @luctielen
    @luctielen 3 ปีที่แล้ว +1

    Thanks, this video in the series especially explained a lot of things that were previously unclear to me before. Kudos!

  • @rufflefpv
    @rufflefpv 4 ปีที่แล้ว +3

    #34:30 the best way to reproduce these kinda bugs is to use a barrier before the actual operation that's supposed to fail

  • @EgnachHelton
    @EgnachHelton 4 ปีที่แล้ว +1

    An interesting way to think about RefCell vs Mutex: In multithreaded code, Mutex force a thread to wait for another thread leaving the critical section. RefCell does the same in single threaded code except that it knows that "the other thread" would never leave the critical section since there's only one thread, so it just terminates the program

  • @artyomostrikov
    @artyomostrikov ปีที่แล้ว

    Thanks for a nice video that gives important knowledge on a really vital topics with examples! I hadn't known some things exist in Rust until watching these videos.

  • @FlaviusAspra
    @FlaviusAspra 4 ปีที่แล้ว

    Question about Cell: why would we use Cell at all? Why not simply pass T or & T to a method, but Cell?

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +1

      You would rarely pass a Cell to a function. But you _would_ pass a &Cell to a function. Or, more commonly, something like an Rc. Those are the cases where Cell is useful - when you have multiple shared references to the Cell, and you want to mutate through _one_ of them.

  •  4 ปีที่แล้ว +2

    Looking forward to the the continuation on mutex and rwlock, great stuff

  • @alexanderadhyatma3126
    @alexanderadhyatma3126 4 ปีที่แล้ว +1

    Thank you Jon, your tutorial are very valuable for me. Also, Plug can be used to run tests from your neovim (no need for another tmux split / tab)

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +1

      Ah, but I prefer to run the tests in a separate and dedicated shell :)

  • @eddieh7962
    @eddieh7962 2 ปีที่แล้ว

    This is so helpful! I knew there were tons of places in rust code where I got yelled at because of having two mutable references, even if those references never existed at the same time in a single threaded app. I guess I can use Cell for situations like this!

  • @Kodlak15
    @Kodlak15 ปีที่แล้ว

    Thank you for the time you have put into this series. It’s been remarkably helpful! My brain is overheating a bit after this one 😅

  • @uoweme5grand
    @uoweme5grand 2 ปีที่แล้ว

    These videos are great.
    Just wondering for the rest of the people watching (those of you who are coming across these concepts for the first time or maybe had only heard about it), does it also take you guys 3 to 4 times the video length to fully understand everything? It takes me a while ...

  • @chaudharypraveen98
    @chaudharypraveen98 2 ปีที่แล้ว

    Awesome. Your way of explanation is magnificent. Thanks for the making tutorial.

  • @daltontinoco7084
    @daltontinoco7084 2 ปีที่แล้ว

    I love how you try and show errors in rust, but rust is like, "Nah dude, I can make it work, trust me!" Unsafe code is hard to write lolol. Good video, earned my sub for sure. Thankyou!

  • @sproccoli
    @sproccoli 9 หลายเดือนก่อน

    this is the perfect way to teach/learn this stuff.

  • @Tamazakis
    @Tamazakis 3 ปีที่แล้ว +4

    std::borrow::Cow. milk it. shake it. drink it... now you own it... no need to return it.
    the magic of Cow

  • @ev3rybodygets177
    @ev3rybodygets177 หลายเดือนก่อน

    Saying cells are nice for flag state knowing refcell was going to use it in that fashion was a nice touch! Clever!

  • @RitobanRoyChowdhury
    @RitobanRoyChowdhury 4 ปีที่แล้ว +3

    You explained Mutex as an RwLock which doesn't distinguish between readers or writers. When might be a practical situation where a Mutex is a better choice than RwLock?

    • @realsong-fake
      @realsong-fake 4 ปีที่แล้ว +1

      From my experience RwLock only makes sense when I have way more read than write on the shared state. Otherwise I find Mutex to be more useful especially under contention. That being said it's best you measure it yourself for your use case.

  • @jonas-mm7em
    @jonas-mm7em 3 ปีที่แล้ว

    Thanks Jon for the great video content once again. It's really interesting and gives concrete examples of harder to grasp Rust notions.

  • @EgnachHelton
    @EgnachHelton 4 ปีที่แล้ว

    6:24 No, Cell has the same memory layout as T thus cannot be use as recursive type storage.

  • @TheHellst0rm
    @TheHellst0rm 2 ปีที่แล้ว

    Thanks for this series! I wonder if you could make a stream for advanced Cargo/build topics? E.g. linking to native libraries, passing compiler parameters, writing Bild scripts, different targets etc. Maybe even something about linkers in general and how Rust interfaces with it (it's often discussed in C courses, but somehow a bit neglected in Rust).

  • @Trequetrum8
    @Trequetrum8 2 ปีที่แล้ว +1

    I'm not sure why `x1.set([1; 1024])` should've failed. Are we assuming that this is being written usize bits at a time into memory and therefore the threads might clobber each other? Certainly I wouldn't expect my compiled code to have a loop here setting the indexes of my array. The compiler should already have inlined the binary representation inside the binary executable.

    • @BboyKeny
      @BboyKeny 2 ปีที่แล้ว

      I was thinking that the threads we're in sync perfectly like thread 1 sets 1 at the first element then thread 2 sets 2 at that place. Then sometimes the first thread starts 1 tick later. That's why it was mostly all 2's and sometimes all 1's.
      But I might be completely wrong about this.

    • @Trequetrum8
      @Trequetrum8 2 ปีที่แล้ว

      @@BboyKeny Maybe we should just compile this and look at what's happening. I would be very surprised to see either thread setting anything via indices.

  • @hscowef4662
    @hscowef4662 4 ปีที่แล้ว

    Very nice video, I was struggling to understand unsafe but watching this after I saw someone share it in the Rust Community Discord server helped me a lot thank you :)

  • @WindBringsMemories
    @WindBringsMemories 3 ปีที่แล้ว +1

    Great tutorial! Liked the debugging part as well :)

  • @billjohnson6863
    @billjohnson6863 3 ปีที่แล้ว +1

    Super useful! Thanks for doing these.

  • @aqua3418
    @aqua3418 2 ปีที่แล้ว +1

    Regarding turning things into *const and *mut.
    It is UB to turn a & reference into an &mut. So *const generally signifies it's an immutable reference, and *mut generally signifies it's a &mut, and it should always be kept the way.
    Of course, if it comes to memory passed from FFI, all bets are off and you'll need to know how the FFI is managing the memory and use proper Rust C structs that properly imitate the c ones, and blah blah blah. Advanced topic for another time. The main point is the Rust ones are easy to remember.

  • @VivekYadav-ds8oz
    @VivekYadav-ds8oz 3 ปีที่แล้ว +1

    28:30 I allocated 1000 50-character long Strings to force the deallocation of the referenced String, the program still doesn't crash/panic.
    I think that's because you don't really have a reference/pointer to the String so much as you have a pointer/reference to the _value_ member variable of the _UnsafeCell_ struct, which still remains a valid String.
    If the Allocator was the reason, it wouldn't have printed "world" in that example it would've still printed "hello".

    • @jonhoo
      @jonhoo  3 ปีที่แล้ว

      It's more likely because deallocation doesn't immediately return the memory to the operating system (which would cause a segfault on access), or wipe that memory in the meantime (which would yield garbage results). Usually, deallocation just means "make available to other allocations", so if you just allocate lots of other strings with the same contents, you'd just expect to have the same memory being overwritten many times with the same value, which means you wouldn't notice when trying to read through an old pointer.

  • @catalinneagu1417
    @catalinneagu1417 2 ปีที่แล้ว +1

    You're a really good teacher. Thank you. Also donated

  • @joeldsouzax
    @joeldsouzax 9 หลายเดือนก่อน

    I think the reason the Cell Test didnt show up that interleaving because you either set the entire cell value as an array of 1 or 2 ...10240 elements, this meant that that OS must have done both concurrently and the one that was did it last was shown in the print.

  • @menfie
    @menfie 4 ปีที่แล้ว

    You do `escape + Shift-O` after opening bracket to get into it. I was doing same before I discovered `let delimitMate_expand_cr=1` option for delimitMate. Highly recommended.

  • @emnul8583
    @emnul8583 2 หลายเดือนก่อน

    I didn't realize just how informative the standard lib documentation was. You've inspired me to study the documentation for the entire Rust standard library. See y'all in about 4 months 🫡

  • @rsalmei
    @rsalmei 2 หลายเดือนก่อน

    @jonhoo At 26:30, it triggered UB! Miri reports an error. It seemed to work because the reference was overwritten (UnsafeCell is repr(transparent)).

  • @tsalVlog
    @tsalVlog 4 ปีที่แล้ว +4

    the deref explanation made a WHOLE bunch things just click for me.

  • @ilyasb4792
    @ilyasb4792 3 ปีที่แล้ว +2

    "Great, it failed, fantastic" 36:29
    It wasn't even ironic lmao

    • @jonhoo
      @jonhoo  3 ปีที่แล้ว +4

      Being able to predict exactly how something won't work is a special kind of good feeling!

  • @karthiknedunchezhiyan1171
    @karthiknedunchezhiyan1171 4 ปีที่แล้ว +2

    Really enjoyed the video, keep going!

  • @karelhrkal8753
    @karelhrkal8753 2 ปีที่แล้ว

    19:50 You don't even need threads to break it. Just make a reference *into* the value stored in the cell, and then replace the value. Boom, instant dangling pointer.

  • @yakupc
    @yakupc 4 ปีที่แล้ว +1

    I respect what you do. These are great videos. You should consider enabling the support button on TH-cam

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +1

      Unfortunately I can't for visa reasons :) But maybe one day!

  • @debasishraychawdhuri
    @debasishraychawdhuri 4 ปีที่แล้ว +1

    I honestly don't understand why this video has so few likes. This is what I have been looking for from the moment I started learning rust.

  • @nammari4276
    @nammari4276 ปีที่แล้ว

    Incredible value. I love these!

  • @FreemanPascal
    @FreemanPascal 2 ปีที่แล้ว

    Jon, is your vim configuration available? I would like to setup vim to work with Rust and yours appears to work really well for that.

  • @MauriceChavez353
    @MauriceChavez353 3 ปีที่แล้ว +1

    Make Jon the rust mascot!

  • @FlaviusAspra
    @FlaviusAspra 4 ปีที่แล้ว

    Question before we dive into Cell: why were the normal references with &, mut&, etc at the syntax level not enough? Or why was the syntax of the language not extended for this interior mutability things?

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +1

      At the language level, you only have two types of references: shared (&) and exclusive (&mut). Rust lets you modify things through the latter, but not the former, because that is the only case where it's _definitely_ safe to do so. If no-one else has a reference, then there cannot be concurrent reads or writes to the value, and you cannot invalidate any other references by changing the thing pointed to. The same is not true for shared references, so Rust does not allow modification through those references. Interior mutability types (types that let you modify through a shared reference) let you do that because they maintain additional, special-case invariants that the compiler cannot check, that ensure that no conflicts arise even if you modify through a shared reference. There are _lots_ of ways to do so (CPU atomics, Mutexes, Cell, RefCell, etc.), and so it's not clear how the language would be "extended" to allow them. They all rely on implementation details of some particular algorithm.

  • @viewing4748
    @viewing4748 2 ปีที่แล้ว

    Does rwlock guarantee any sort of fairness between readers and writers or is that something you'll have to implement yourself? Can starvation happen with rwlock? In general should I be using rwlock for threaded apps or using crates liek tokio and rayon? are there other popular crates do that kind of stuff with?

  • @Agryphos
    @Agryphos 2 ปีที่แล้ว +1

    I was able to get the interleaving that Jon attempted around 34:00 by using [u8; 320000] as inner values for the Cell. Most of the time the result was uniform but I got interleaving every now and then

    • @Knirin
      @Knirin 2 ปีที่แล้ว +1

      That is a separate bug. Probably one in the compiler not Your or Jon’s code. Interleaving of the values is not the expected behavior.
      The expected behavior is what Jon got. An array that is unpredictably either all 1s or all 2s. The data race should be in the final assignment not in the array creation.
      If interleaving was the expected behavior data corruption from any two threads using the stack in a similar time window would cause a lot of havoc.

    • @Agryphos
      @Agryphos 2 ปีที่แล้ว

      @@Knirin That's what I thought at first as well, that both would be creating a full array and the race is who gets to set the pointer. Interesting to know that is correct and that I had a separate bug here.

    • @Knirin
      @Knirin 2 ปีที่แล้ว

      @@Agryphos Having had more time to think about it, I think the issue is in the memory allocator. It may not be thread safe for stack allocations in excess of the CPU’s L1 cache. Which are generally under 64KB. You may need to get a few times over the L1 cache size to reliably see an issue.
      I am kind of surprised that Rust tries sticking multi KB data structures on the stack. Every other compiled language I have dealt with is throwing things onto the heap before a data structure is a KB in size, usually transparently to the user. Transparently until your compilation target doesn’t have an operating system or you are trying to write a kernel in said language anyway. The jokes about C being an easier to read assembler aren’t far off.

  • @filipbielejec9038
    @filipbielejec9038 3 ปีที่แล้ว

    From ~55:00 - is there a reason you cannot implement Drop (and DropMut) for RefCell directly and going via these helper Ref/RefMut types?

  • @logicprojects
    @logicprojects 4 ปีที่แล้ว +1

    Great work, Thanks for all you do

  • @mithradates
    @mithradates 3 ปีที่แล้ว

    Interesting, the code at 1:34:38 doesn't generate an error anymore!

  • @sortof3337
    @sortof3337 ปีที่แล้ว +1

    1:41:13 example aldo doesn't compile anymore but this compiles
    ```
    fn main() {
    let foo;
    let mut t;
    t = String::from("Hello");
    foo = Rc::new(Foo { v: &mut t });
    }
    ```

  • @MuhamadAzmy
    @MuhamadAzmy 4 ปีที่แล้ว

    I think the problem with the "not failing" test regarding large array writing is that both threads allocate the array first on their stack. then ONLY one set call to replace the value in set. So it ends up either one of the 2 threads commits this "set" last, hence only its value shows up.

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +1

      They should be racing on their call to set, and each set sets the array _by value_, which should mean a full memcpy. Arrays in Rust aren't pointers, they are values on the stack, so since Cell contains an array, it really contains all those bytes, so set has to memcpy.

    • @MuhamadAzmy
      @MuhamadAzmy 4 ปีที่แล้ว

      @@jonhoo Oh, that makes much sense, thank you for clearing this up :)

  • @sharperguy
    @sharperguy 4 ปีที่แล้ว

    I think I've seen you use these types in other videos. However, I don't recall you explaining in detail WHY you needed it in that case. I haven't seen many of your videos yet, though. Are there any where you are writing an example, and using these types, and explaining in detail why it's needed?

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +1

      I think the standard library documentation gives pretty decent examples of when you might need each one, but the basic summary is:
      - You need Rc/Arc for when you want to share access to a value for an indeterminate amount of time.
      - You need Cell for when you want the ability to replace a shared value.
      - You need RefCell/Mutex for when you need to mutate a shared value.

  • @sodiumsalt
    @sodiumsalt 3 ปีที่แล้ว

    Might I suggest delimitMate nvim extension? You seem to be manually expanding {} a lot, and it has automatic {} expansion on carriage return.

    • @jonhoo
      @jonhoo  3 ปีที่แล้ว

      I despise anything that automatically inserts things into my editing buffer, because my experience is that they inevitably get in the way. I'd rather type the extra character :p Generally I'm not bottlenecked by typing but by my own brain!

  • @karelhrkal8753
    @karelhrkal8753 2 ปีที่แล้ว

    1:27:49 I keep wondering: why do you need to manually drop the "inner" value? Will the constructed Box not destroy it?

    • @karelhrkal8753
      @karelhrkal8753 2 ปีที่แล้ว

      1:21:39 Oh, "inner" is a raw pointer and dropping it doesn't (really) do anything. The actual data of T is still dropped only once by the Box. Thanks.

  • @manojbabu3640
    @manojbabu3640 2 ปีที่แล้ว

    To check if you have all 2's or 1's set, may be you could print sum.

  • @pauloalmeida2126
    @pauloalmeida2126 2 ปีที่แล้ว

    Came here for PhatomData's explanation; First thing Jon said: "This is going to make your head hurt";....... Missing accomplished bro :)

  • @cr74life96
    @cr74life96 2 ปีที่แล้ว

    This series is just perfect :)

  • @jacklong2182
    @jacklong2182 2 ปีที่แล้ว

    Great tutorial , thanks for share

  • @user-ov5nd1fb7s
    @user-ov5nd1fb7s 4 ปีที่แล้ว +1

    Great videos. Keep em coming.

  • @veetaha
    @veetaha 4 ปีที่แล้ว +1

    @Jon Gjengset, might I ask you to increase the mic volume? It is too low to be able to listen to w/o headphones on both of my laptops...

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว

      Hmm, that doesn't sound right. Is the volume in the TH-cam player set too low perhaps? The audio recording level is pretty standard for videos as far as I can tell?

    • @RandomUser311
      @RandomUser311 4 ปีที่แล้ว

      @@jonhoo it's somewhat low but not terribly bad on my tablet as well.

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว

      Interesting. My recording level is where all the sources I've found on the topic say they should be, so not sure what's happening then.. I'll see if there's anything I can do about it for next video!

    • @veetaha
      @veetaha 4 ปีที่แล้ว

      @@jonhoo Well even having the volume slightly higher won't hurt because lowering it is way easier than reaching the ceiling of the max volume and not being able to increase it to the satisfying level. I should say that listening with headphones is alright, but maybe it's just my fridge is overly loud in the background ;D

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +7

      I actually just now found the problem! It looks like TH-cam only performs loudness _reduction_, not boosting, so it requires that you perform loudness normalization before uploading if your input signal is relatively quiet (which my voice is). I sadly can't fix the _current_ video, but will make sure that future videos have an appropriate audio level!

  • @willemvanderveen7567
    @willemvanderveen7567 3 ปีที่แล้ว +1

    Keep it up man great videos!

  • @michaelritsema7108
    @michaelritsema7108 4 ปีที่แล้ว

    Are you sure what you say at 29:00 is accurate? It seems like this was the exact thing Rust is trying to prevent from happening.
    I'm new to rust but it seems to be it works and will always work because you are still pointed to the same underlying UnsafeCell type. Perhaps because mem::replace uses the same memory address not matter what you replace it with.
    I'd be interested to hear your feedback on this.

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +1

      Yes, absolutely, the statement there is accurate. And yes, Rust prevents those things from happening _when you're writing safe code_ . But here, we are specifically writing unsafe code, which gives us the ability to fiddle with raw pointers, and there it is up to _us_ to uphold the safety contract. What I'm trying to explain at that segment is why we need to not give out references from Cell - it's because doing so would mean that the user could write code that references invalid memory without anything in _their_ code being unsafe.
      I think maybe part of what has you confused is that String itself is also a pointer to some heap allocation. When we print a string, it's true that the location of the UnsafeCell hasn't changed (so the pointer to the String hasn't changed), but the pointed-to value by that String _has_ changed. This might be clearer if I'd dereferenced the pointer to get a &str directly to the string on the heap, rather than just go one level deep to get a &String.
      Hope that helps a little!

  • @chinoto1
    @chinoto1 3 ปีที่แล้ว +1

    A less idealistic description of `unsafe {...}` is "I want to do things that have the potential to be unsafe", whether there has been any attempt to verify the code is safe isn't guaranteed.

  • @VivekYadav-ds8oz
    @VivekYadav-ds8oz 3 ปีที่แล้ว +1

    I still don't get why you would use a _Cell_ type for storing the refcount. We're in a single-threaded environment (as far as Rc is concerned), so there should be no race conditions, we can just modify refcount through the pointer!

    • @jonhoo
      @jonhoo  3 ปีที่แล้ว +1

      Even if a type is !Send, it's not okay to give out a mutable (i.e., exclusive) reference to a value contained inside that type. Consider a single-threaded program that walks a cyclic data structure, and ends up vising a single node twice. It then ends up with two concurrent mutable references to a single value. Now imagine it passes those to a method like mem::swap - bad things will likely happen. Worse yet, the compiler is allowed to optimize based on mutable references being exclusive, so it might apply an optimization that assumes that the second reference will not change (since we have an exclusive reference), but that optimization is invalid since the same value can change by changing through the first reference. It is undefined behavior to _ever_ have multiple mutable references to a single value concurrently, no matter whether you're in single-threaded or multi-threaded context. Hence the use of Cell, which allows mutation through a shared reference (though note it never _gives out_ a mutable reference).

    • @VivekYadav-ds8oz
      @VivekYadav-ds8oz 3 ปีที่แล้ว +1

      @@jonhoo Damn, is it really undefined behaviour to do
      unsafe { *refcount += 1 } ? That's wild if that's true. It seems like a very simple case where we don't pass a reference (mutable or shared) to anyone. We modify it, but only through our code. Using a _Cell_ here would be more out of principle than it being actually needed, imo.

    • @jonhoo
      @jonhoo  3 ปีที่แล้ว +1

      @@VivekYadav-ds8oz Mutating directly through a shared reference without UnsafeCell is *always* undefined behavior, precisely because the compiler may optimized based on the assumption that the pointer isn't shared, which would be invalid (and this produce incorrect code) if it was shared. You could do it with UnsafeCell if you *knew* it was truly not shared (even on the same thread), because that prevents some of those optimizations.

  • @juchemz
    @juchemz ปีที่แล้ว

    Is there any reason why Cell doesn't use Clone instead of Copy for the bound on get?

  • @VivekYadav-ds8oz
    @VivekYadav-ds8oz 3 ปีที่แล้ว +1

    Also why being Send is opt-out? That seems very dangerous that it isn't opt-in. What if I'm just implementing Structs as I do everyday, and one of them isn't really safe to send to threads, and I forget to opt-out of it. Then send it over? Kinda curious why that's the case.

    • @jonhoo
      @jonhoo  3 ปีที่แล้ว +1

      Send is an auto-trait, so it's not _quite_ that it's opt-out. A type is !Send if _any_ of its members are !Send. And raw pointers for example are !Send. So I think it's very unlikely for you to have a type that truly isn't thread safe that is still Send unless you use unsafe. And if you use unsafe, thread-safety is one of the (many) things you have to check. Even then though, the auto-implementation will probably make your type !Send already, just because it almost certainly includes some kind of raw pointer.

    • @VivekYadav-ds8oz
      @VivekYadav-ds8oz 3 ปีที่แล้ว +1

      @@jonhoo That's good to know that in practicality my types would be !Send unless I make them Send by unsafe impl.
      But I'm still confused why it was decided that Send would be an auto-trait? I have to wrap all my types in Arc anyways before sending (it I want shared ownership). So it's not like trivial structs benefit from this auto-trait, as I have to wrap my type with a Send type anyways.
      My guess is that you can't even send a clone of your type if you wanted because you have to move the clone too. That _would_ get annoying.

    • @jonhoo
      @jonhoo  3 ปีที่แล้ว +1

      I'm not sure I follow? Being able to send a type to another thread is very common, such as passing it to a closure in thread::spawn or sending it over a channel. Those cases don't require sharing, just sending, and it'd be really unfortunate if you had to wrap all of those in Arc unnecessarily.

  • @secondengineer9814
    @secondengineer9814 ปีที่แล้ว

    So the only difference between Cell and UnsafeCell seems to be the function signatures?

  • @cryptomando
    @cryptomando 2 ปีที่แล้ว

    Thank you Jon

  • @oskarberndal5310
    @oskarberndal5310 4 ปีที่แล้ว +2

    Hello Jon! Thanks for the great video =) I love coding along with these.
    It never really became clear to me why I would prefer an 'Rc' instead of a normal shared reference '&'. It seems to me (and this is probably wrong) that the benifits of '&T' and 'Rc' are similar except that the compiler sometimes can infer when all the references through '&T' are dropped and we get the 'T' back. However, we can never really depend on all the references through 'Rc' being dropped at any point in our code, so we could as well just use a '&T' which never gets dropped (if that makes sense).
    I would be very happy if you could elaborate why I'm wrong here but you are a busy man and I understand if you have other things to do - anyway thanks for the awesomely prepared videos cheers from sweden =)

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +9

      Hi Oskar! Glad you're enjoying the videos :) So, the big difference between &T and Rc is that the former borrows a value, whereas the latter has "partial ownership" of a value. With a &T, the compiler knows, at compile time, what value the reference borrows, and how long that value is live for. And it checks that the &T never outlives the value (for example because the function holding it returns), and that it never conflicts with another type of borrow (&mut T). With Rc, there is no value "being borrowed", not really. There is an owned value that lives on the heap (think Box), where the Rcs organize among themselves so that only when the last Rc goes away is the Box dropped. By upholding that contract, every Rc can reference the T safely, since they all do so with shared references, and the target is always valid (since it's owned and on the heap).

  • @SomeNullBytes
    @SomeNullBytes 2 ปีที่แล้ว

    At 28:16, is it possible that it was due to short string optimization?

  • @bongjunjang5683
    @bongjunjang5683 4 ปีที่แล้ว +1

    pure gold

  • @farseendeveloper461
    @farseendeveloper461 4 ปีที่แล้ว

    How do you move firefox's navigation bar to the bottom? Didn't it get messed up when they changed the address bar recently?

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว

      I'm continuously updating my userChrome.css over at github.com/jonhoo/configs/blob/master/gui/.mozilla/firefox/chrome/userChrome.css :)

  • @dionysis_
    @dionysis_ ปีที่แล้ว

    This is so useful! 🙏

  • @VivekYadav-ds8oz
    @VivekYadav-ds8oz 3 ปีที่แล้ว +1

    I thought if I was writing *fn foo(bar: T) {}* , I was either taking ownership of a non-reference type, or taking a static reference! After all, I thought, that's why you can define it like this: *fn foo(bar: &T) {}* , because you needed to distinguish b/w the two! So now wherever I assumed that I am the owner of type T and used _unsafe_ assuming this invariant, I now need to be careful that I might just have been passed a reference :(

    • @jonhoo
      @jonhoo  3 ปีที่แล้ว +1

      Ah, yes, is _very_ much not guaranteed to be owned. T + 'static is guaranteed to be usable for as long as you hold on to it, so that's perhaps what you're after. Think of it as " means _any_ type T", and &T is more constrained, as it only allows _references_ to any type.

  • @huxleyrummy9544
    @huxleyrummy9544 4 ปีที่แล้ว

    Just started watching the video. As Jon says, "it seems antithetical". From knowing some Rust, it sure seems. If so, is these Cell and RefCell types made with some special compiler support or are they just regular vanilla rust with maybe some unsafes?? Haven't watched the rest of video though neither searched theses types source code :D

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +2

      Hopefully the rest of the video explained it sufficiently!

  • @maspe1
    @maspe1 4 ปีที่แล้ว

    Why doesn't Cell have trait bounds on its T? What good is a Cell around a T that doesn't implement Copy?

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +1

      In general Rust prefers to have bounds on impls, not structs. There are a couple of reasons for that, but the primary one is that it avoids reduces how many times the bounds have to be repeated throughout your code base, and the code base of anyone embedding your type.

  • @gangwang2547
    @gangwang2547 4 ปีที่แล้ว

    really great, what's your neovim color scheme

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +1

      It's called gruvbox, and I'm specifically using the "hard" dark version :)

  • @michaelritsema7108
    @michaelritsema7108 4 ปีที่แล้ว

    34:52 I expect that isn't mixing 1's and 2's because Rust would first build total arrays and set would just be swapping pointers.

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +1

      Ah, no, there are no pointers here. [0; 1024] in Rust is an array on the stack, so when you overwrite one, the values of the stack indeed have to be copied over.

    • @MarcelloUrbani
      @MarcelloUrbani 4 ปีที่แล้ว

      Don't you copy the whole array on the heap though? To really get a mixed value you should mutate array cells

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +1

      @@MarcelloUrbani In theory, the two threads should both be doing a memcpy into the same stack memory, which should in turn mean that they can contend on any given memory location as they walk the arrays to write them back.

    • @MarcelloUrbani
      @MarcelloUrbani 4 ปีที่แล้ว

      Yep, you're right.

  • @alexzander__6334
    @alexzander__6334 2 ปีที่แล้ว

    great videos. thanks.

  • @naveendavisv
    @naveendavisv 4 ปีที่แล้ว

    What is exclusive reference means ? I heard about mutable reference (&mut T ) and immutable reference (&T)

    • @maboesanman
      @maboesanman 4 ปีที่แล้ว +5

      Exclusive reference is a synonym for mutable reference.
      Edit: this is true in a practical sense, but generally you choose one or the other (mutable or exclusive) depending on what property of the reference you are trying to emphasize. This doesn’t affect the code directly, but it helps to understand design intentions when one description is chosen over the other.

    • @PaulSebastianM
      @PaulSebastianM 4 ปีที่แล้ว +1

      Immutable non-exclusive reference, mutable exclusive reference. Non-exclusive meaning more than one reference can be created at a time, exclusive meaning only one reference can be created at a time.

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว +4

      The other comments are quite right - exclusive is a synonym (and arguably a better word for) mutable references. Take a look at docs.rs/dtolnay/0.0.7/dtolnay/macro._02__reference_types.html. You can read "mut" as "mutually exclusive" if that helps.

    • @benedyktjaworski9877
      @benedyktjaworski9877 4 ปีที่แล้ว

      @@jonhoo I like dtolnay’s simplified convention to consistently use the terms ‘mutable reference’ (since when you have it - you can always mutate what’s behind it, and it’s in line with the keyword) but ‘shared references’ (since you always can share them and thus you need those in multithreaded contexts, and _sometimes_ also mutate through them). _Mutable (because exclusive)_ vs _shared (and possibly mutable)_ isn’t symmetric but it does click for me. I can mutate with &mut T (but that’s it, no sharing); I can share (and might be able to mutate) with &T.

  • @karelhrkal8753
    @karelhrkal8753 2 ปีที่แล้ว

    Hey, I added a comment the other day with a link to the Rust playground and I don't see it anymore. Is TH-cam automatically deleting comments with links or something?

  • @bowarc
    @bowarc ปีที่แล้ว

    unsafe is more like a 'source: trust me bro' tag than something not safe to use

  • @workharderkomrade9662
    @workharderkomrade9662 4 ปีที่แล้ว

    Quick question: how do you set a dark theme for the docs ?

    • @jonhoo
      @jonhoo  4 ปีที่แล้ว

      Click the little "brush" to the left of the search field at the top :)

  • @leolin652
    @leolin652 ปีที่แล้ว

    Thank you!!