{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Utility Traits " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Drop\n", "\n", "The `Drop` trait is used to run some code when a value goes out of scope. It is similar to the destructor in C++." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ ":dep libc" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Opening file - FileDescriptor { fd: 43, file_name: \"file_descriptor_test1.txt\" }\n", "Writing \"Hello, world!\" to file FileDescriptor { fd: 43, file_name: \"file_descriptor_test1.txt\" }\n", "Opening file - FileDescriptor { fd: 44, file_name: \"file_descriptor_test2.txt\" }\n", "Writing \"Rust is awesome!\" to file FileDescriptor { fd: 44, file_name: \"file_descriptor_test2.txt\" }\n", "Closing file - FileDescriptor { fd: 43, file_name: \"file_descriptor_test1.txt\" }\n", "Writing \"Goodbye, world!\" to file FileDescriptor { fd: 44, file_name: \"file_descriptor_test2.txt\" }\n", "Closing file - FileDescriptor { fd: 44, file_name: \"file_descriptor_test2.txt\" }\n" ] }, { "data": { "text/plain": [ "Ok(())" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "//use std::os::unix::io::{RawFd, FromRawFd, AsRawFd};\n", "//use std::fs::File;\n", "use std::io::{self};\n", "use std::os::raw::c_int;\n", "use std::str;\n", "\n", "static mut FD: i32 = 42;\n", "\n", "#[derive(Debug)]\n", "pub struct FileDescriptor {\n", " fd: c_int,\n", " file_name: String\n", "}\n", "\n", "impl FileDescriptor {\n", " // Create a new FileDescriptor from a RawFd\n", " pub fn new(path: &str) -> Self {\n", " // real implementation under unix\n", " // let fd = unsafe { libc::open(path.as_ptr() as *const i8, libc::O_RDWR) };\n", " // FileDescriptor { fd }\n", "\n", " let fd: FileDescriptor;\n", " unsafe {\n", " FD += 1;\n", " fd = FileDescriptor{ fd: FD, file_name: path.to_string() };\n", " }\n", " println!(\"Opening file - {:?}\", fd);\n", " fd\n", " }\n", "\n", " // Write to the file descriptor\n", " pub fn write(&self, buf: &[u8]) -> io::Result {\n", " // real implementation\n", " // let mut file = unsafe { File::from_raw_fd(self.fd) };\n", " // let result = file.write(buf);\n", " // std::mem::forget(file); // Prevent the file from being closed\n", " // result\n", "\n", " println!(r###\"Writing \"{}\" to file {:?}\"###, str::from_utf8(buf).unwrap(), self);\n", " return Ok(buf.len());\n", " }\n", "}\n", "\n", "impl Drop for FileDescriptor {\n", " fn drop(&mut self) {\n", " // real implementation\n", " // unsafe {\n", " // libc::close(self.fd);\n", " // }\n", "\n", " println!(\"Closing file - {:?}\", self);\n", " }\n", "}\n", "\n", "fn main() -> io::Result<()> {\n", " let mut fd1 = FileDescriptor::new(\"file_descriptor_test1.txt\"); // 1 is the file descriptor for stdout\n", " {\n", " fd1.write(b\"Hello, world!\")?;\n", " \n", " let fd2 = FileDescriptor::new(\"file_descriptor_test2.txt\");\n", " fd2.write(\"Rust is awesome!\".as_bytes())?;\n", "\n", " fd1 = fd2;\n", " }\n", "\n", " fd1.write(\"Goodbye, world!\".as_bytes())?;\n", "\n", " Ok(())\n", "}\n", "\n", "main()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sized Trait" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Sized Types\n", "\n", "* A sized type is one whose values all have the same size in memory. \n", " * Almost all types in Rust are sized:\n", " * `u64` takes eight bytes\n", " * `(f32, f32, f32)` tuple takes twelve bytes \n", " * Structs & Enums are sized\n", " * `String` and `Vec` own a heap-allocated buffer whose size can vary, the `Vec` value itself is a pointer to the buffer, its capacity, and its length, so `Vec` is a sized type.\n", "* All sized types implement the `std::marker::Sized` trait, which has no methods or associated types. \n", " * Rust implements it automatically for all types to which it applies; you can’t implement it yourself\n", " * Traits of this sort are called *marker traits*, because the Rust language itself uses them to mark certain types as having characteristics of interest.\n", "* The only use for `Sized` is as a bound for type variables: a bound like `T: Sized` requires `T` to be a type whose size is known at compile time.\n", "\n", "* Most generic type variables should be restricted to `Sized` types. Therefore it is the implicit default trait in Rust:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "struct AStruct {\n", " a: T,\n", "}\n", "\n", "// is equivalent to\n", "\n", "struct AStruct {\n", " a: T,\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Unsized Types\n", "\n", "* However, Rust also has a few unsized types whose values are not all the same size. \n", " * The string slice type `str` (without an `&`) is unsized. The string literals `\"diminutive\"` and `\"big\"` are `str` objects that occupy ten and three bytes.\n", " * Array slice types like `[T]` (again, without an `&`) are unsized. A shared reference like `&[u8]` can point to a `[u8]` slice of any size. Because the `str` and `[T]` types denote sets of values of varying sizes, they are unsized types.\n", " * `dyn Type` is unsized - but `&dyn std::io::Write` and `Box` are fat pointers (two words wide: a pointer to a slice also carries the slice’s length, and a trait object also carries a pointer to a vtable of method implementations) and are sized\n", "\n", "If you plan to use unsized type in a generic context, you need to use the `?Sized` bound. This is a special trait bound that relaxes the requirement that the type must be sized. " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "struct BoxedValue {\n", " value: Box,\n", "}\n", "\n", "let boxed: BoxedValue<[i32]> = BoxedValue {\n", " value: Box::new([1, 2, 3]),\n", "};" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Clone" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* The `std::clone::Clone` trait is for types that can make copies of themselves. `Clone` is defined as follows:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "mod explain {\n", " trait Clone: Sized {\n", " fn clone(&self) -> Self;\n", " fn clone_from(&mut self, source: &Self) { \n", " *self = source.clone(); \n", " }\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* The `clone()` method should construct an independent copy of `self` and return it. Since this method’s return type is `Self`, and functions may not return unsized values, the `Clone` trait itself extends the `Sized` trait: this has the effect of bounding implementations’ `Self` types to be `Sized`.\n", "* The `clone_from()` method modifies `self` into a copy of source. The default definition of `clone_from()` simply clones source, and then moves that into `*self`. This is implemented by default as `*self = source.clone()`, but may be more efficient for types that can reuse allocated resources." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Deriving `Clone`\n", "\n", "If your `Clone` implementation simply applies clone to each field or element of your type, and then constructs a new value from those clones, and the default definition of `clone_from` is good enough, then put `#[derive(Clone)]` above your type definition:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "#[derive(Debug, Clone, PartialEq)]\n", "struct Data {\n", " name: String,\n", " id: i32,\n", " data: Vec,\n", "}\n", "\n", "let user_data = Data { name: \"John\".to_string(), id: 42, data: vec![1, 2, 3] };\n", "let mut backup_data = user_data.clone();\n", "\n", "assert_eq!(user_data, backup_data);\n", "\n", "backup_data.name = \"Jane\".to_string();\n", "\n", "assert_ne!(user_data, backup_data);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Copy\n", "\n", "* A type is `Copy` if it implements the `std::marker::Copy` marker trait, which is defined as follows:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "mod explain {\n", " trait Copy: Clone { }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Implementing the `Copy` trait is a marker that a type’s values can be duplicated simply by copying bits. This trait has no methods, and the compiler enforces the property that `Copy` types cannot implement the `Drop` trait." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "#[derive(Clone)]\n", "struct ValueData {\n", " id: u64,\n", " data: f64\n", "}\n", "\n", "impl Copy for ValueData { }" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* The simplest way to make a type `Copy` is to derive it:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "#[derive(Copy, Clone)]\n", "struct ValueData {\n", " id: u64,\n", " data: f64\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Deref & DerefMut" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* You can specify how dereferencing operators like `*` and `.` behave on your types by implementing the `std::ops::Deref` and `std::ops::DerefMut` traits. \n", "* Pointer types like `Box` and `Rc` implement these traits so that they can behave as Rust’s built-in pointer types do." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "The type of the variable user_data was redefined, so was lost.\n", "The type of the variable backup_data was redefined, so was lost.\n" ] } ], "source": [ ":dep num-complex\n", "\n", "use num_complex::Complex;\n", "\n", "let z: Box> = Box::new(Complex{re: 1.0, im: 2.0});\n", "\n", "assert_eq!((*z).re, 1.0);\n", "assert_eq!(z.re, 1.0); // deref coercion" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "mod explain {\n", " trait Deref {\n", " type Target: ?Sized;\n", " fn deref(&self) -> &Self::Target;\n", " }\n", " \n", " trait DerefMut: Deref {\n", " fn deref_mut(&mut self) -> &mut Self::Target;\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* The `Deref` and `DerefMut` are used for **deref coercions**. \n", " * Since deref takes a `&Self` reference and returns a `&Self::Target` reference, Rust uses this to **automatically convert references of the former type into the latter**. \n", " * In other words, if inserting a deref call would prevent a type mismatch, Rust inserts one for you. Implementing `DerefMut` enables the corresponding conversion for mutable references. These are called the **deref coercions: one type is being “coerced” into behaving as another**.\n", "* Examples of *deref coersions*:\n", " * if you have a `Rc` value `r`, you can call `r.len()` even though `Rc` does not have a `len` method. This is because `Rc` implements `Deref`, so Rust automatically inserts a `*` dereference to convert `r` into a `&String` reference, and then calls `len()` on that reference.\n", " * you can call `split_at()` on `String` values, even though `String` does not have a `split_at()` method. This is because `String` implements `Deref`, so Rust automatically inserts a `*` dereference to convert the `&String` into a `&str` reference, and then calls `split_at()` on that reference.\n", " * if you have some `Rc` value r, and want to apply `String::find` to it, you can simply write `r.find('?')`, instead of `(*r).find('?')`: the method call implicitly borrows `r`, and `&Rc` coerces to `&String`, because `Rc` implements `Deref`" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "use std::rc::Rc;\n", "\n", "let r: Rc = Rc::new(\"HelloWorld\".to_string());\n", "\n", "r.len() // r will coerce to &String" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(\"Hello\", \"World\")" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "r.split_at(5) // r will coerce to &String, then &String will coerce to &str which has the method split_at" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Implementing `Deref` & `DerefMut`" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "struct Selector {\n", " /// Elements available in this `Selector`.\n", " elements: Vec,\n", "\n", " /// The index of the \"current\" element in `elements`. A `Selector`\n", " /// behaves like a pointer to the current element.\n", " current: usize\n", "}\n", "\n", "use std::ops::{Deref, DerefMut};\n", "\n", "impl Deref for Selector {\n", " type Target = T;\n", " fn deref(&self) -> &T {\n", " &self.elements[self.current]\n", " }\n", "}\n", "\n", "impl DerefMut for Selector {\n", " fn deref_mut(&mut self) -> &mut T {\n", " &mut self.elements[self.current]\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "let mut s = Selector { elements: vec!['x', 'y', 'z'], current: 2 };\n", "\n", "// Because `Selector` implements `Deref`, we can use the `*` operator to\n", "// refer to its current element.\n", "assert_eq!(*s, 'z');\n", "\n", "// Assert that 'z' is alphabetic, using a method of `char` directly on a\n", "// `Selector`, via deref coercion.\n", "assert!(s.is_alphabetic());\n", "\n", "// Change the 'z' to a 'w', by assigning to the `Selector`'s referent.\n", "*s = 'w';\n", "\n", "assert_eq!(s.elements, ['x', 'y', 'w']);" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ugly\n" ] } ], "source": [ "let s = Selector { \n", " elements: vec![\"good\", \"bad\", \"ugly\"],\n", " current: 2 \n", "};\n", "\n", "fn show_it(thing: &str) { \n", " println!(\"{}\", thing); \n", "}\n", "\n", "show_it(&s); // OK because `&Selector` can be coerced to `&T`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Rust do not apply coercion when checking whether the bound `T: Trait` is satisfied" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "use std::fmt::Display;\n", "\n", "fn show_it_generic(thing: T) { println!(\"{}\", thing); }\n", "\n", "//show_it_generic(&s); // error[E0277]: the trait bound `&Selector<&str>: Display` is not satisfied" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ugly\n" ] } ], "source": [ "show_it_generic(&s as &str); // OK" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Default\n", "\n", "* Some types have a reasonably obvious default value: \n", " * the default vector or string is empty, \n", " * the default number is zero, \n", " * the default `Option` is `None`, and so on. \n", "* Types like this can implement the `std::default::Default` trait:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "mod explain {\n", " trait Default {\n", " fn default() -> Self;\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Explicit implementaiton of `Default`:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Complex { re: 0.0, im: 0.0 }" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#[derive(Debug)]\n", "struct Complex {\n", " re: T,\n", " im: T,\n", "}\n", "\n", "impl Default for Complex where T: Default {\n", " fn default() -> Self {\n", " Complex { re: Default::default(), im: Default::default() }\n", " }\n", "}\n", "\n", "Complex::::default()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* If a type `T` implements `Default`, then the standard library implements `Default` automatically for `Rc`, `Arc`, `Box`, `Cell`, `RefCell`, `Cow`, `Mutex`, and `RwLock`. The default value for the type `Rc`, for example, is an `Rc` pointing to the default value for type `T`.\n", "\n", "* If all the element types of a tuple type implement `Default`, then the tuple type does too, defaulting to a tuple holding each element’s default value.\n", "\n", "* Rust does not implicitly implement `Default` for struct types, but if all of a struct’s fields implement `Default`, you can implement `Default` for the struct automatically using `#[derive(Default)]`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Usage of common use of `Default` is to produce default values for structs that represent a large collection of parameters, most of which you won’t usually need to change" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Player { name: \"John\", age: 42, level: 0, score: 0, record_score: 0 }" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#[derive(Default, Debug)]\n", "struct Player {\n", " name: String,\n", " age: u8,\n", " level: u8,\n", " score: u32,\n", " record_score: u32\n", "}\n", "\n", "let player = Player { \n", " name: \"John\".to_string(),\n", " age: 42,\n", " ..Default::default()\n", "};\n", "\n", "player" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## AsRef and AsMut\n", "\n", "* When a type implements `AsRef`, that means you can borrow a `&T` from it efficiently. `AsMut` is the analogue for mutable references. " ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "mod explain {\n", " trait AsRef {\n", " fn as_ref(&self) -> &T;\n", " }\n", " \n", " trait AsMut {\n", " fn as_mut(&mut self) -> &mut T;\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* For example:\n", " * `Vec` implements `AsRef<[T]>` \n", " * `String` implements `AsRef` and you can also borrow a `String`’s contents as an array of bytes, so `String` implements `AsRef<[u8]>` as well." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* `AsRef` is typically used **to make functions more flexible in the argument types they accept**." ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Processing data: SomeData { values: [1, 2, 3] }\n", "Processing data: [1, 2, 3]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Processing data: [1, 2, 3, 4, 5, 6]\n" ] } ], "source": [ "use std::fmt::Debug;\n", "\n", "#[derive(Debug)]\n", "struct SomeData {\n", " values: Vec\n", "}\n", "\n", "impl AsRef<[i32]> for SomeData {\n", " fn as_ref(&self) -> &[i32] {\n", " &self.values\n", " }\n", "}\n", "\n", "fn process_data + Debug>(data: T) {\n", " println!(\"Processing data: {:?}\", data);\n", "}\n", "\n", "let data = SomeData { values: vec![1, 2, 3] };\n", "process_data(&data);\n", "\n", "let v = vec![1, 2, 3];\n", "process_data(&v);\n", "\n", "let a = [1, 2, 3, 4, 5, 6];\n", "process_data(&a);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Borrow and BorrowMut\n", "\n", "* The `std::borrow::Borrow` trait is similar to `AsRef`: if a type implements `Borrow`, then its borrow method efficiently borrows a `&T` from it. \n", "* But `Borrow` imposes more restrictions: a type should implement `Borrow` only when a `&T` hashes and compares the same way as the value it’s borrowed from. (Rust doesn’t enforce this; it’s just the documented intent of the trait.) This makes Borrow valuable in dealing with keys in hash tables and trees or when dealing with values that will be hashed or compared for some other reason.\n", " * `String` implements `AsRef`, `AsRef<[u8]>`, and `AsRef`, but those three target types will generally have different hash values. Only the `&str` slice is guaranteed to hash like the equivalent `String`, so `String` implements only `Borrow`." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "mod explain {\n", " trait Borrow {\n", " fn borrow(&self) -> &Borrowed;\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## From and Into" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* The `std::convert::From` and `std::convert::Into` traits represent **conversions that consume a value of one type and return a value of another**. \n", "* Whereas the `AsRef` and `AsMut` traits borrow a reference of one type from another, `From` and `Into` take ownership of their argument, transform it, and then return ownership of the result back to the caller." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "mod explain {\n", " trait From: Sized {\n", " fn from(other: T) -> Self;\n", " }\n", "\n", " trait Into: Sized {\n", " fn into(self) -> T;\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* The standard library automatically implements the trivial conversion from each type to itself: every type `T` implements `From` and `Into`\n", "\n", "* Although the traits simply provide two ways to do the same thing, they lend themselves to different uses." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "### Using Into\n", "* You generally use `Into` to make your functions more flexible in the arguments they accept. For example, if you write:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Pinging address: 23.21.68.141\n", "Ok(true)\n", "Pinging address: 66.146.219.98\n", "Ok(true)\n", "Pinging address: 208.118.235.148\n", "Ok(true)\n" ] } ], "source": [ "use std::net::Ipv4Addr;\n", "fn ping(address: A) -> std::io::Result\n", " where A: Into\n", "{\n", " let ipv4_address = address.into();\n", " // rest implementation\n", " println!(\"Pinging address: {}\", ipv4_address);\n", " Ok(true)\n", "}\n", "\n", "println!(\"{:?}\", ping(Ipv4Addr::new(23, 21, 68, 141))); // pass an Ipv4Addr\n", "println!(\"{:?}\", ping([66, 146, 219, 98])); // pass a [u8; 4]\n", "println!(\"{:?}\", ping(0xd076eb94_u32)); // pass a u32" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Using From" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " * The `from` method serves as a generic constructor for producing an instance of a type from some other single value. \n", " * For example, rather than `Ipv4Addr` having two methods named `from_array` and `from_u32`, it simply implements `From<[u8;4]>` and `From`, allowing us to write:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "let addr1 = Ipv4Addr::from([66, 146, 219, 98]);\n", "let addr2 = Ipv4Addr::from(0xd076eb94_u32);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Given an appropriate `From` implementation, the standard library automatically implements the corresponding `Into` trait. \n", "* When you define your own type, if it has single-argument constructors, you should write them as implementations of `From` for the appropriate types; you’ll get the corresponding `Into` implementations for free.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* `from` and `into` methods take ownership of their arguments (conversion can reuse the original values)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[72, 101, 108, 108, 111, 32, 82, 117, 115, 116, 121, 33]\n" ] } ], "source": [ "let text = \"Hello Rusty!\".to_string();\n", "let bytes: Vec = text.into();\n", "println!(\"{:?}\", bytes);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* `From` and `Into` are infallible traits—their API requires that conversions will not fail" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-1454759936\n" ] } ], "source": [ "let huge = 2_000_000_000_000i64;\n", "let smaller: i32 = huge as i32;\n", "println!(\"{:?}\", smaller);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## TryFrom & TryInto\n", "\n", "* `TryFrom` and `TryInto` are the fallible cousins of `From` and `Into` and are similarly reciprocal; implementing `TryFrom` means that `TryInto` is implemented as well." ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "mod explain {\n", " pub trait TryFrom: Sized {\n", " type Error;\n", " fn try_from(value: T) -> Result;\n", " }\n", "\n", " pub trait TryInto: Sized {\n", " type Error;\n", " fn try_into(self) -> Result;\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2147483647" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let huge = 2_000_000_000_000i64;\n", "let smaller = huge.try_into().unwrap_or(i32::MAX);\n", "smaller" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2147483647" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let smaller: i32= huge.try_into().unwrap_or_else(|_| {\n", " if huge >= 0 {\n", " i32::MAX\n", " } else {\n", " i32::MIN\n", " }\n", "});\n", "smaller" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ToOwned" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* `The std::borrow::ToOwned` trait provides a slightly looser (than `Clone` trait) way to convert a reference to an owned value:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "mod explain {\n", " trait ToOwned {\n", " type Owned: std::borrow::Borrow;\n", " fn to_owned(&self) -> Self::Owned;\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Unlike `clone`, which must return exactly `Self`, `to_owned` can return anything you could borrow a `&Self` from: the `Owned` type must implement `Borrow`. You can borrow a `&[T]` from a `Vec`, so `[T]` can implement `ToOwned>`, as long as `T` implements `Clone`, so that we can copy the slice’s elements into the vector" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Clone on Write (COW)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A `Cow` either borrows a shared reference to a `B`, or owns a value which we could borrow such a reference. This can be useful when you have a value that is sometimes owned and sometimes borrowed." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "mod explain {\n", " enum Cow<'a, B: ?Sized + 'a> \n", " where B: ToOwned \n", " {\n", " Borrowed(&'a B),\n", " Owned(::Owned),\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The requested item could not be found.\n", "An error occurred: Disk full\n" ] } ], "source": [ "use std::borrow::Cow;\n", "\n", "enum Error {\n", " NotFound,\n", " PermissionDenied,\n", " OutOfMemory,\n", " Other(String),\n", "}\n", "\n", "fn describe_error(error: &Error) -> Cow<'static, str> {\n", " match error {\n", " Error::NotFound => Cow::Borrowed(\"The requested item could not be found.\"),\n", " Error::PermissionDenied => Cow::Borrowed(\"You do not have permission to access the requested item.\"),\n", " Error::OutOfMemory => Cow::Borrowed(\"The system is out of memory.\"),\n", " Error::Other(message) => Cow::Owned(format!(\"An error occurred: {}\", message)),\n", " }\n", "}\n", "\n", "let error_not_found = Error::NotFound;\n", "let error_other = Error::Other(\"Disk full\".to_string());\n", "\n", "println!(\"{}\", describe_error(&error_not_found));\n", "println!(\"{}\", describe_error(&error_other));" ] } ], "metadata": { "kernelspec": { "display_name": "Rust", "language": "rust", "name": "rust" }, "language_info": { "codemirror_mode": "rust", "file_extension": ".rs", "mimetype": "text/rust", "name": "rust", "pygment_lexer": "rust", "version": "" } }, "nbformat": 4, "nbformat_minor": 2 }