Crust of Rust: Smart Pointers and Interior Mutability

Поділитися
Вставка
  • Опубліковано 28 лип 2024
  • In this fourth Crust of Rust video, we cover smart pointers and interior mutability, by re-implementing the Cell, RefCell, and Rc types from the standard library. As part of that, we cover when those types are useful, how they work, and what the equivalent thread-safe versions of these types are. In the process, we go over some of the finer details of Rust's ownership model, and the UnsafeCell type. We also dive briefly into the Drop Check rabbit hole (doc.rust-lang.org/nightly/nom...) before coming back up for air.
    This is definitely a more technically advanced stream than some of the earlier ones, and may be a little harder to follow. I apologize for that in advance! Please do leave questions here or on Discord and I'll try to help explain what's going on.
    You can find the final code at gist.github.com/jonhoo/7cfdfe... and the Discord at / discord
    0:00:00 Introduction
    0:01:11 Discord
    0:02:31 Agenda
    0:03:50 Interior Mutability
    0:07:47 Cell
    0:23:39 Trying to Test Cell
    0:40:17 UnsafeCell
    0:41:21 RefCell
    0:54:21 RefCell Smart Pointer
    1:06:27 Rc (reference counted ptr)
    1:23:49 NonNull
    1:31:55 PhantomData and Drop Check
    1:44:25 ?Sized Briefly
    1:47:30 Thread Safety
    1:54:20 Copy-on-Write (Cow)
    You can watch the live version with comments at • Crust of Rust: Smart P...
  • Наука та технологія

КОМЕНТАРІ • 202

  • @derekdreery
    @derekdreery 4 роки тому +193

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

    • @jonhoo
      @jonhoo  4 роки тому +26

      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.

  • @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 Рік тому

      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.

  • @nikis05
    @nikis05 4 роки тому +36

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

  • @chsblue2
    @chsblue2 3 роки тому +7

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

  • @VivekYadav-ds8oz
    @VivekYadav-ds8oz 2 роки тому +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 2 роки тому +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 14 годин тому

      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.

  • @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.

  • @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.

  • @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

  • @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.

  • @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.

  • @Codeaholic1
    @Codeaholic1 2 роки тому +2

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

  • @luctielen
    @luctielen 3 роки тому +1

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

  • @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

  • @ChristopherBreeden85
    @ChristopherBreeden85 4 роки тому +68

    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 10 місяців тому

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

  • @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.

  • @kevincarvalhodejesus4473
    @kevincarvalhodejesus4473 3 місяці тому

    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!

  • @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 :)

  • @monawoka97
    @monawoka97 3 місяці тому

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

  • @jonas-mm7em
    @jonas-mm7em 2 роки тому

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

  • @sputnick1
    @sputnick1 8 місяців тому

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

  • @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 😅

  • @chaudharypraveen98
    @chaudharypraveen98 Рік тому

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

  • @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.

  •  4 роки тому +2

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

  • @WindBringsMemories
    @WindBringsMemories 3 роки тому +1

    Great tutorial! Liked the debugging part as well :)

  • @billjohnson6863
    @billjohnson6863 3 роки тому +1

    Super useful! Thanks for doing these.

  • @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!

  • @karthiknedunchezhiyan1171
    @karthiknedunchezhiyan1171 3 роки тому +2

    Really enjoyed the video, keep going!

  • @logicprojects
    @logicprojects 4 роки тому +1

    Great work, Thanks for all you do

  • @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 :)

  • @nammari4276
    @nammari4276 Рік тому

    Incredible value. I love these!

  • @user-ov5nd1fb7s
    @user-ov5nd1fb7s 4 роки тому +1

    Great videos. Keep em coming.

  • @cr74life96
    @cr74life96 2 роки тому

    This series is just perfect :)

  • @sproccoli
    @sproccoli 5 місяців тому

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

  • @willemvanderveen7567
    @willemvanderveen7567 3 роки тому +1

    Keep it up man great videos!

  • @catalinneagu1417
    @catalinneagu1417 2 роки тому +1

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

  • @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!

  • @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).

  • @EgnachHelton
    @EgnachHelton 3 роки тому +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

  • @user-je3qh9gu7o
    @user-je3qh9gu7o 10 місяців тому

    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 8 місяців тому

      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

  • @yakupc
    @yakupc 4 роки тому +1

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

    • @jonhoo
      @jonhoo  4 роки тому +1

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

  • @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.

  • @smartislav
    @smartislav 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

  • @Sina-xw4xp
    @Sina-xw4xp 4 роки тому

    This was advanced stuff :)

  • @uoweme5grand
    @uoweme5grand Рік тому

    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 ...

  • @menfie
    @menfie 3 роки тому

    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.

  • @jacklong2182
    @jacklong2182 2 роки тому

    Great tutorial , thanks for share

  • @MauriceChavez353
    @MauriceChavez353 3 роки тому +1

    Make Jon the rust mascot!

  • @dionysis_
    @dionysis_ Рік тому

    This is so useful! 🙏

  • @EgnachHelton
    @EgnachHelton 3 роки тому

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

  • @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.

  • @joeldsouzax
    @joeldsouzax 5 місяців тому

    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.

  • @cryptomando
    @cryptomando 2 роки тому

    Thank you Jon

  • @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.

  • @VivekYadav-ds8oz
    @VivekYadav-ds8oz 2 роки тому +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  2 роки тому

      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.

  • @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?

  • @alexzander__6334
    @alexzander__6334 2 роки тому

    great videos. thanks.

  • @tsalVlog
    @tsalVlog 4 роки тому +4

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

  • @karelhrkal8753
    @karelhrkal8753 Рік тому

    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.

  • @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.

  • @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!

  • @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.

  • @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

  • @mithradates
    @mithradates 3 роки тому

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

  • @Agryphos
    @Agryphos Рік тому +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 Рік тому +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 Рік тому

      @@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 Рік тому

      @@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.

  • @dickheadhex418
    @dickheadhex418 3 роки тому +1

    I think the array example for threading racing didn't work because the array values aren't written individually to the unsafe cell, instead each time set() is called the underlying unsafecell is assigned a new array so it will either be all 2s or all 1s but never interleaved. But also because of how fast the computer is, the order is maintained usually so it shows 2s everytime. is this the right way to look at it?

  • @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?

  • @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?

    • @everestshadow
      @everestshadow 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.

  • @SomeNullBytes
    @SomeNullBytes 2 роки тому

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

  • @naveendavisv
    @naveendavisv 4 роки тому

    thanks for sharing.

  • @bongjunjang5683
    @bongjunjang5683 4 роки тому +1

    pure gold

  • @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 9 місяців тому

    Thank you!!

  • @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 :)

  • @juchemz
    @juchemz 9 місяців тому

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

  • @karelhrkal8753
    @karelhrkal8753 Рік тому

    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 Рік тому

      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.

  • @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!

  • @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 :)

  • @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!

  • @manojbabu3640
    @manojbabu3640 Рік тому

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

  • @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 :)

  • @busydying
    @busydying 4 роки тому +1

    Hey, Jon :) I watch all of your streams in recordings, not because I can't do it in live, but because I'm such a slow learner, that I constantly pause it to think or google something. The whole experience is almost perfect, but I lacking seeing the chat, sometimes it feels as I'm missing some context with it. So if there's any possibility to add it into the video, it would be great.

    • @jonhoo
      @jonhoo  4 роки тому +2

      There's a link to the video with chat included at the bottom of the video description :)

    • @busydying
      @busydying 4 роки тому

      @@jonhoo Ah, sorry, I missed it somehow, thanks :)

  • @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 UA-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 UA-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!

  • @Trequetrum8
    @Trequetrum8 Рік тому +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 Рік тому

      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 Рік тому

      @@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.

  • @pauloalmeida2126
    @pauloalmeida2126 2 роки тому

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

  • @secondengineer9814
    @secondengineer9814 Рік тому

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

  • @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.

  • @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!

  • @slmjkdbtl
    @slmjkdbtl 4 роки тому

    Which LSP client (vim plugin) and server (rls/analyzer?) are you using?

    • @jonhoo
      @jonhoo  4 роки тому +1

      I'm using coc in neovim with rust-analyzer

    • @slmjkdbtl
      @slmjkdbtl 4 роки тому

      @@jonhoo thank you!

  • @cogidtjr
    @cogidtjr 3 роки тому

    Thanks!!!

  • @maspe1
    @maspe1 3 роки тому

    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  3 роки тому +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.

  • @VivekYadav-ds8oz
    @VivekYadav-ds8oz 2 роки тому +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  2 роки тому +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 2 роки тому +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  2 роки тому +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.

  • @karelhrkal8753
    @karelhrkal8753 Рік тому

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

  • @aqua3418
    @aqua3418 2 роки тому

    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 2 роки тому +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  2 роки тому +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 2 роки тому +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  2 роки тому +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.

  • @echoptic775
    @echoptic775 2 роки тому

    Whats the terminal ur using?

    • @jonhoo
      @jonhoo  2 роки тому

      I'm using fish shell in the Alacritty terminal.

  • @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).

  • @VivekYadav-ds8oz
    @VivekYadav-ds8oz 2 роки тому +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  2 роки тому +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.

  • @bowarc
    @bowarc 9 місяців тому

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