# Rust for Rustaceans ![rw-book-cover](https://m.media-amazon.com/images/I/8194Ew8gVrS._SY160.jpg) ## Metadata - Author: Jon Gjengset - Full Title: Rust for Rustaceans - Category: #rust #programming-best-practices #async-programming #multi-threading ## Highlights - A value in Rust is the combination of a type and an element of that type’s domain of values. A value can be turned into a sequence of bytes using its type’s representation, but on its own you can think of a value more like what you, the programmer, meant. ([Location 638](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=638)) - The most common place to store a value is a variable, which is a named value slot on the stack. ([Location 644](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=644)) - A pointer is a value that holds the address of a region of memory, so the pointer points to a place. ([Location 645](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=645)) - High-level models are useful when thinking about code at the level of lifetimes and borrows, while low-level models are good for when you are reasoning about unsafe code and raw pointers. ([Location 673](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=673)) - If a new variable is declared with the same name as a previous one, they are still considered distinct variables. This is called shadowing—the later variable “shadows” the former by the same name. ([Location 704](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=704)) - The three most important regions for the purposes of writing Rust code are the stack, the heap, and static memory. ([Location 728](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=728)) - The stack is a segment of memory that your program uses as scratch space for function calls. Each time a function is called, a contiguous chunk of memory called a frame is allocated at the top of the stack. Near the bottom of the stack is the frame for the main function, and as functions call other functions, additional frames are pushed onto the stack. ([Location 730](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=730)) - The bytes that make up the values of the function’s local variables are not immediately wiped, but it’s not safe to access them as they may have been overwritten by a subsequent function call whose frame overlaps with the reclaimed one. ([Location 734](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=734)) - Stack frames, and crucially the fact that they eventually disappear, are very closely tied to the notion of lifetimes in Rust. Any variable stored in a frame on the stack cannot be accessed after that frame goes away, so any reference to it must have a lifetime that is at most as long as the lifetime of the frame. ([Location 738](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=738)) - The heap is a pool of memory that isn’t tied to the current call stack of the program. Values in heap memory live until they are explicitly deallocated. This is useful when you want a value to live beyond the lifetime of the current function’s frame. ([Location 741](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=741)) - Since allocations from the heap do not go away when a function returns, you can allocate memory for a value in one place, pass the pointer to it to another thread, and have that thread safely continue to operate on that value. ([Location 748](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=748)) - The primary mechanism for interacting with the heap in Rust is the Box type. ([Location 751](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=751)) - If you forget to deallocate heap memory, it will stick around forever, and your application will eventually eat up all the memory on your machine. This is called leaking memory and is usually something you want to avoid. ([Location 754](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=754)) - You can allocate that on the heap and explicitly leak it with Box::leak to get a 'static reference to it. ([Location 756](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=756)) - Static memory is really a catch-all term for several closely related regions located in the file your program is compiled into. These regions are automatically loaded into your program’s memory when that program is executed. Values in static memory live for the entire execution of your program. ([Location 758](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=758)) - A bound like T: 'static indicates that the type parameter T is able to live for however long we keep it around for, up to and including the remaining execution of the program. ([Location 772](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=772)) - You may wonder how const differs from static. The const keyword declares the following item as constant. Constant items can be completely computed at compile time, and any code that refers to them is replaced with the constant’s computed value during compilation. A constant has no memory or other storage associated with it (it is not a place). You can think of constant as a convenient name for a particular value. ([Location 780](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=780)) - Rust’s memory model centers on the idea that all values have a single owner—that is, exactly one location (usually a scope) is responsible for ultimately deallocating each value. This is enforced through the borrow checker. ([Location 785](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=785)) - Most primitive types in Rust, such as the integer and floating-point types, are Copy. ([Location 793](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=793)) - The rules for the order in which to drop are fairly simple: variables (including function arguments) are dropped in reverse order, and nested values are dropped in source-code order. ([Location 824](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=824)) - References are pointers that come with an additional contract for how they can be used, such as whether the reference provides exclusive access to the referenced value, or whether the referenced value may also have other references point to it. ([Location 837](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=837)) - A shared reference, &T, is, as the name implies, a pointer that may be shared. ([Location 840](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=840)) - With mutable references, the Rust compiler is again allowed to make full use of the contract that the reference comes with: the compiler assumes that there are no other threads accessing the target value, whether through a shared reference or a mutable one. In other words, it assumes that the mutable reference is exclusive. ([Location 854](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=854)) - A mutable reference lets you mutate only the memory location that the reference points to. Whether you can mutate values that lie beyond the immediate reference depends on the methods provided by the type that lies between. ([Location 868](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=868)) - The primary difference between owning a value and having a mutable reference to it is that the owner is responsible for dropping the value when it is no longer necessary. ([Location 878](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=878)) - Some types provide interior mutability, meaning they allow you to mutate a value through a shared reference. ([Location 904](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=904)) - These normally fall into two categories: those that let you get a mutable reference through a shared reference, and those that let you replace a value given only a shared reference. ([Location 906](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=906)) - The first category consists of types like Mutex and RefCell, which contain safety mechanisms to ensure that, for any value they give a mutable reference to, only one mutable reference (and no shared references) can exist at a time. ([Location 907](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=907)) - The Cell type in the standard library is an interesting example of safe interior mutability through invariants. It is not shareable across threads and never gives out a reference to the value contained in the Cell. Instead, the methods all either replace the value entirely or return a copy of the contained value. ([Location 918](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=918)) - A lifetime is really a name for a region of code that some reference must be valid for. ([Location 928](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=928)) - At the heart of Rust lifetimes is the borrow checker. Whenever a reference with some lifetime 'a is used, the borrow checker checks that 'a is still alive. ([Location 931](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=931)) - First, if your type also implements Drop, then dropping your type counts as a use of any lifetime or type your type is generic over. ([Location 989](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=989)) - Variance is the reason why, in Java, you can pass a Turtle to a function that accepts an Animal if Turtle is a subtype of Animal, or why, in Rust, you can pass a &'static str to a function that accepts a &'a str. ([Location 1015](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1015)) - There are three kinds of variance: covariant, invariant, and contravariant. ([Location 1025](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1025)) - A type is covariant if you can just use a subtype in place of the type. For example, if a variable is of type &'a T, you can provide a value of type &'static T to it, because &'a T is covariant in 'a. &'a T is also covariant in T, so you can pass a &Vec<&'static str> to a function that takes &Vec<&'a str>. ([Location 1025](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1025)) - Some types are invariant, which means that you must provide exactly the given type. &mut T is an example of this—if a function takes a &mut Vec<&'a str>, you cannot pass it a &mut Vec<&'static str>. That is, &mut T is invariant in T. ([Location 1029](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1029)) - The last category, contravariance, comes up for function arguments. Function types are more useful if they’re okay with their arguments being less useful. ([Location 1034](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1034)) - Variance becomes relevant when you consider how generic lifetime parameters interact with the borrow checker. ([Location 1042](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1042)) - alignment, which dictates where the bytes for a type can be stored. ([Location 1093](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1093)) - on a 64-bit CPU, most values are accessed in chunks of 8 bytes (64 bits), with each operation starting at an 8-byte-aligned address. This is referred to as the CPU’s word size. ([Location 1100](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1100)) - A naturally aligned value is one whose alignment matches its size. So, for example, for an 8-byte load, the provided address would need to be 8-byte-aligned. ([Location 1111](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1111)) - how the compiler decides on the in-memory representation, known as the layout, of a type. ([Location 1120](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1120)) - Rust provides a repr attribute you can add to your type definitions to request a particular in-memory representation for that type. ([Location 1122](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1122)) - padding—bytes with an indeterminate value that are ignored in user code—into ([Location 1145](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1145)) - You can do that using the attribute #[repr(align(n))]. A common use case for this is to ensure that different values stored contiguously in memory (like in an array) end up in different cache lines on the CPU. That way, you avoid false sharing, which can cause huge performance degradations in concurrent programs. ([Location 1172](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1172)) - False sharing occurs when two different CPUs access different values that happen to share a cache line; while they can theoretically operate in parallel, they both end up contending to update the same single entry in the cache. ([Location 1174](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1174)) - Tuple Represented like a struct with fields of the same type as the tuple values in the same order. ([Location 1179](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1179)) - Array Represented as a contiguous sequence of the contained type with no padding between the elements. ([Location 1180](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1180)) - Union Layout is chosen independently for each variant. Alignment is the maximum across all the variants. ([Location 1181](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1181)) - Enumeration Same as union, but with one additional hidden shared field that stores the enum variant discriminant. The discriminant is the value the code uses to determine which of the enum variants a given value holds. The size of the discriminant field depends on the number of variants. ([Location 1182](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1182)) - You may have come across the marker trait Sized in various odd corners of the Rust documentation and in error messages. Usually, it comes up because the compiler wants you to provide a type that is Sized, but you (apparently) did not. ([Location 1186](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1186)) - Most types in Rust implement Sized automatically—that is, they have a size that’s known at compile time—but two common types do not: trait objects and slices. ([Location 1187](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1187)) - size depends on some information that is known only when the program runs and not at compile time, which is why they are called dynamically sized types (DSTs). ([Location 1189](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1189)) - The compiler requires types to be Sized nearly everywhere. ([Location 1194](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1194)) - The way to bridge this gap between unsized and sized types is to place unsized types behind a wide pointer (also known as a fat pointer). ([Location 1199](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1199)) - Box and Arc also support storing wide pointers, which is why they both support T: ?Sized. ([Location 1208](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1208)) - Traits are a key piece of Rust’s type system—they are the glue that allows types to interoperate even though they don’t know about each other at the time they are defined. ([Location 1210](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1210)) - When you write a type or function that is generic over T, you’re really telling the compiler to make a copy of that type or function for each type T. When you construct a Vec<i32> or a HashMap<String, bool>, the compiler essentially copy-pastes the generic type and all its implementation blocks and replaces all instances of each generic parameter with the concrete type you provided. ([Location 1218](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1218)) - But there is no one address we could use for any type, so we need to have one copy for each type, each with its own address to jump to. This is referred to as static dispatch, since for any given copy of the method, the address we are “dispatching to” is known statically. ([Location 1237](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1237)) - This process of going from a generic type to many non-generic types is called monomorphization, and it’s part of the reason generic Rust code usually performs just as well as non-generic code. ([Location 1244](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1244)) - The alternative to static dispatch is dynamic dispatch, which enables code to call a trait method on a generic type without knowing what that type is. ([Location 1265](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1265)) - Well, with dynamic dispatch, the caller simply tells you. If you replace impl Pattern with &dyn Pattern, you tell the caller that they must give two pieces of information for this argument: the address of the pattern and the address of the is_contained_in method. ([Location 1268](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1268)) - You’ll notice that when we opted in to dynamic dispatch using the dyn keyword, we had to place an & in front of it. The reason is that we no longer know at compile time the size of the pattern type that the caller passes in, so we don’t know how much space to set aside for it. In other words, dyn Trait is !Sized, where the ! means not. ([Location 1280](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1280)) - The combination of a type that implements a trait and its vtable is known as a trait object. ([Location 1291](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1291)) - Most traits can be turned into trait objects, but not all. ([Location 1292](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1292)) - Methods with a where Self: Sized bound are exempted when checking if a trait is object-safe. ([Location 1304](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1304)) - Dynamic dispatch cuts compile times, since it’s no longer necessary to compile multiple copies of types and methods, and it can improve the efficiency of your CPU instruction cache. However, it also prevents the compiler from optimizing for the specific types that are used. ([Location 1305](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1305)) - Broadly speaking, though, you’ll want to use static dispatch in your libraries and dynamic dispatch in your binaries. ([Location 1312](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1312)) - use an associated type if you expect only one implementation of the trait for a given type, and use a generic type parameter otherwise. ([Location 1320](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1320)) - With a generic trait, users must always specify all the generic parameters and repeat any bounds on those parameters. ([Location 1323](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1323)) - With associated types, on the other hand, the compiler needs to know only the type that implements the trait, and all the associated types follow (since there is only one implementation). This means the bounds can all live in the trait itself and do not need to be repeated on use. ([Location 1330](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1330)) - These rules exist to preserve the coherence property: for any given type and method, there is only ever one correct choice for which implementation of the method to use for that type. ([Location 1337](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1337)) - The coherence property ensures that the compiler never ends up in these situations and never has to make these choices: there will always be exactly one obvious choice. ([Location 1343](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1343)) - Ideally, we would like to find some set of rules that balances the desire for downstream crates to implement upstream traits for their own types against the desire for upstream crates to be able to add implementations of their own traits without breaking downstream code. ([Location 1350](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1350)) - In Rust, the rule that establishes that balance is the orphan rule. Simply stated, the orphan rule says that you can implement a trait for a type only if the trait or the type is local to your crate. ([Location 1358](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1358)) - The orphan rule allows you to implement traits over a range of types with code like impl<T> MyTrait for T where T: and so on. This is a blanket implementation—it is not limited to just one particular type but instead applies to a wide range of types. ([Location 1365](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1365)) - For the purposes of the orphan rule, fundamental types may as well not exist—they are effectively erased before the orphan rule is checked in order to allow you to, for example, implement IntoIterator for &MyType. ([Location 1373](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1373)) - When you write generic code yourself, it will almost certainly include trait bounds, as otherwise your code cannot do much with the type it is generic over. ([Location 1413](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1413)) - First and foremost, trait bounds do not have to be of the form T: Trait where T is some type your implementation or type is generic over. ([Location 1416](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1416)) - To enable you to write bounds like this, Rust lets you refer to associated types of a type using the syntax Type::AssocType. For example, we can refer to I’s Item type using I::Item. If a type has multiple associated types by the same name, such as if the trait that provides the associated type is itself generic (and therefore there are many implementations), you can disambiguate with the syntax <Type as Trait>::AssocType. ([Location 1440](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1440)) - This type of bound is known as a higher-ranked trait bound, and it’s particularly useful in association with the Fn traits. For example, say you want to be generic over a function that takes a reference to a T and returns a reference to inside that T. If you write F: Fn(&T) -> &U, you need to provide a lifetime for those references, but you really want to say “any lifetime as long as the output is the same as the input.” Using a higher-ranked lifetime, you can write F: for<'a> Fn(&'a T) -> &'a U to say that for any lifetime 'a, the bound must hold. ([Location 1447](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1447)) - Usually, we use traits to denote functionality that multiple types can support; a Hash type can be hashed by calling hash, a Clone type can be cloned by calling clone, and a Debug type can be formatted for debugging by calling fmt. ([Location 1466](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1466)) - Some traits, called marker traits, instead indicate a property of the implementing type. Marker traits have no methods or associated types and serve just to tell you that a particular type can or cannot be used in a certain way. ([Location 1468](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1468)) - The standard library has a number of these in the std::marker module, including Send, Sync, Copy, Sized, and Unpin. Most of these (all except Copy) are also auto-traits; the compiler automatically implements them for types unless the type contains something that does not implement the marker trait. ([Location 1471](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1471)) - Similar to marker traits are marker types. These are unit types (like struct MyMarker;) that hold no data and have no methods. Marker types are useful for, well, marking a type as being in a particular state. ([Location 1480](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1480)) - This is because of type inference, where the compiler decides what type to use based on what type the code the type appears in evaluates to. ([Location 1489](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1489)) - To handle situations like this, Rust supports existential types. Chances are, you have already seen existential types in action. All functions marked as async fn or with a return type of impl Trait have an existential return type: the signature does not give the true type of the return value, just a hint that the function returns some type that implements some set of traits that the caller can rely on. And crucially, the caller can only rely on the return type implementing those traits, and nothing else. ([Location 1495](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1495)) - This behavior is what gives existential types their name: we are asserting that there exists some concrete type that matches the signature, and we leave it up to the compiler to find what that type is. The compiler will usually then go figure that out by applying type inference on the body of the function. ([Location 1504](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1504)) - writing idiomatic interfaces in Rust, whether the users of those interfaces are your own code or other developers using your library. These essentially boil down to four principles: your interfaces should be unsurprising, flexible, obvious, and constrained. ([Location 1533](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1533)) - Users aren’t allowed to implement a foreign trait (like Clone) for a foreign type like one from your interface. They would instead need to wrap your interface type in their own type, and even then it may be quite difficult to write a reasonable implementation without access to the type’s internals. ([Location 1569](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1569)) - First among these standard traits is the Debug trait. Nearly every type can, and should, implement Debug, even if it only prints the type’s name. ([Location 1571](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1571)) - Tied in close second are the Rust auto-traits Send and Sync (and, to a lesser extent, Unpin). If a type does not implement one of these traits, it should be for a very good reason. A type that is not Send can’t be placed in a Mutex and can’t be used even transitively in an application that contains a thread pool. ([Location 1575](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1575)) - The next set of nearly universal traits you should implement is Clone and Default. ([Location 1581](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1581)) - Finally, for most types, it makes sense to implement the serde crate’s Serialize and Deserialize traits. ([Location 1595](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1595)) - There are two things that set Copy apart from the other traits mentioned. The first is that users do not generally expect types to be Copy; quite to the contrary, they tend to expect that if they want two copies of something, they have to call clone. Copy changes the semantics of moving a value of the given type, which might surprise the user. This ties in to the second observation: it is very easy for a type to stop being Copy, because Copy types are highly restricted. ([Location 1600](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1600)) - Rust does not automatically implement traits for references to types that implement traits. To phrase this a different way, you cannot generally call fn foo<T: Trait>(t: T) with a &Bar, even if Bar: Trait. This is because Trait may contain methods that take &mut self or self, which obviously cannot be called on &Bar. ([Location 1607](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1607)) - For this reason, when you define a new trait, you’ll usually want to provide blanket implementations as appropriate for that trait for &T where T: Trait, &mut T where T: Trait, and Box<T> where T: Trait. ([Location 1612](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1612)) - For any type that can be iterated over, consider implementing IntoIterator for both &MyType and &mut MyType where applicable. This makes for loops work with borrowed instances of your type as well out of the box, just like users would expect. ([Location 1616](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1616)) - Rust does not have object inheritance in the classical sense. However, the Deref trait and its cousin AsRef both provide something a little like inheritance. These traits allow you to have a value of type T and call methods on some type U by calling them directly on the T-typed value if T: Deref<Target = U>. This feels like magic to the user, and is generally great. ([Location 1619](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1619)) - If you provide a relatively transparent wrapper type (like Arc), there’s a good chance you’ll want to implement Deref so that users can call methods on the inner type by just using the . operator. ([Location 1623](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1623)) - You may also have come across the Borrow trait, which feels very similar to Deref and AsRef but is really a bit of a different beast. Specifically, Borrow is tailored for a much narrower use case: allowing the caller to supply any one of multiple essentially identical variants of the same type. ([Location 1628](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1628)) - In general, Borrow is intended only for when your type is essentially equivalent to another type, whereas Deref and AsRef are intended to be implemented more widely for anything your type can “act as.” ([Location 1636](https://readwise.io/to_kindle?action=open&asin=B0957SWKBS&location=1636))