├── 200ThingsToRememberAboutRust.md ├── PopularRustCrates.txt ├── README.md ├── RustSyntacticSugarList.md └── TurbofishAndAssociatedTypes.md /200ThingsToRememberAboutRust.md: -------------------------------------------------------------------------------- 1 | 200 Things Every Rust Programmer Should Remember 2 | 3 | (Robust Rust) 4 | 5 | By Dragos Ruiu 6 | (2023 May 1) 7 | 8 | 1. Borrowing allows you to temporarily access an owned value without transferring ownership, maintaining memory safety. 9 | 2. References represent borrowed access to a value and can be either mutable (`&mut T`) or immutable (`&T`). 10 | 3. You can create an immutable reference to a value using `&` and a mutable reference using `&mut`. 11 | 4. Multiple immutable references to a value can coexist, but no mutable references are allowed simultaneously. 12 | 5. A mutable reference must be the only reference to a value, preventing any other mutable or immutable references. 13 | 6. Lifetimes are used to specify the valid scope of a reference, ensuring that it doesn't outlive the value it points to. 14 | 7. Function arguments and return values can use references to avoid transferring ownership of values. 15 | 8. Rust enforces borrowing rules at compile-time, ensuring memory safety without runtime overhead. 16 | 9. Borrowing rules help avoid common memory issues like data races, dangling pointers, and use-after-free. 17 | 10. Borrowing helps prevent unnecessary cloning of data, which improves performance and reduces memory usage. 18 | 11. Immutable references allow shared read access to data, while mutable references provide exclusive write access. 19 | 12. You can dereference a reference using the `*` operator to access the underlying value. 20 | 13. When using mutable references, Rust enforces that they must be unique within their scope, providing aliasing XOR mutability. 21 | 14. When passing references to functions or storing them in structs, you may need to use explicit lifetimes to specify their relationship with the borrowed data. 22 | 15. The `'static` lifetime refers to references that live for the entire duration of the program. 23 | 16. The Rust compiler can infer lifetimes in many cases, but sometimes you'll need to use lifetime annotations to help the compiler understand the relationships between references. 24 | 17. The borrowing rules help avoid iterator invalidation issues that can occur in other languages. 25 | 18. When working with mutable references, you should be careful not to create self-referential data structures, which can lead to undefined behavior. 26 | 19. If you encounter borrowing-related issues, you can use Rust's interior mutability pattern, such as `RefCell` or `Mutex`, to provide safe, controlled mutable access to data. 27 | 20. Rust's ownership system enables automatic memory management without the need for a garbage collector. 28 | 21. The `Cell` and `RefCell` types allow for interior mutability, enabling mutation of values through shared references within a safe API. 29 | 22. The `Mutex` and `RwLock` types provide thread-safe interior mutability for concurrent programming, ensuring data consistency. 30 | 23. When returning references from functions, the lifetime of the return value must not exceed the lifetime of the borrowed data. 31 | 24. Rust's `Deref` and `DerefMut` traits allow you to define custom reference types with their own dereference behavior. 32 | 25. The `Box` type allows for heap-allocated values with unique ownership, and you can create references to the boxed data. 33 | 26. When working with references to trait objects, you need to use explicit lifetimes to specify the relationship between the trait object and the borrowed data. 34 | 27. The `Arc` and `Rc` types provide shared ownership of heap-allocated values, and you can create references to the shared data. 35 | 28. Rust's borrowing rules apply to both the stack and the heap. 36 | 29. Lifetime elision is a feature where the Rust compiler can infer lifetimes for references in function signatures, reducing verbosity. 37 | 30. The `std::mem::drop` function can be used to explicitly drop a value and its associated resources, even when it's still borrowed. 38 | 31. You can use the `std::mem::replace` function to replace a value in memory while maintaining a reference to the original value. 39 | 32. Rust's `std::mem::swap` function enables you to safely swap the contents of two mutable references without violating borrowing rules. 40 | 33. The `std::mem::transmute` function can be used to convert between types with the same size and alignment, but it should be used cautiously, as it can lead to undefined behavior if used incorrectly with references. 41 | 34. Rust's `std::mem::ManuallyDrop` type can be used to wrap a value and prevent its `Drop` implementation from being called, allowing you to manage the lifetime of borrowed data manually. 42 | 35. Borrowing slices, such as `&[T]` and `&mut [T]`, allows for efficient, safe access to contiguous sequences of elements. 43 | 36. The `Cow` (Clone on Write) type allows for efficient, lazy cloning of data when mutation is needed while still using references. 44 | 37. Rust's borrowing rules prevent iterator invalidation issues by ensuring that the underlying collection cannot be modified while iterating. 45 | 38. You can use closures with references as arguments to create higher-order functions that operate on borrowed data. 46 | 39. The `as_ref` and `as_mut` methods can be used to convert owned values into references, simplifying code that works with both owned and borrowed data. 47 | 40. The `?` operator can be used with references to `Result` and `Option` types to propagate errors or `None` values, enabling concise error handling with borrowed data. 48 | 41. The `Pin` type and `Unpin` trait help manage references to heap-allocated data, ensuring the data won't move while a reference is in use. 49 | 42. Rust's `PhantomData` type can be used to associate lifetimes with generic types, providing a safe API for borrowing data. 50 | 43. The `std::ptr` module provides raw pointers (`*const T` and `*mut T`) for unsafe code, which bypasses borrowing rules and requires manual management. 51 | 44. Rust's `NonNull` type provides a non-null raw pointer with additional compile-time safety checks compared to regular raw pointers. 52 | 45. Using the `Borrow` and `BorrowMut` traits, you can create custom reference types that interact seamlessly with Rust's borrowing system. 53 | 46. The `From` and `Into` traits allow for flexible, ergonomic conversions between types, including references. 54 | 47. Rust's slice patterns allow for pattern matching and destructuring of borrowed slices, providing a concise way to work with slices. 55 | 48. The `std::iter` module provides a rich set of iterator adapters that can work with borrowed data, enabling powerful and data transformations. 56 | 49. Rust's `std::iter::repeat` function can be used to create an iterator that yields a borrowed value infinitely, providing a simple way to generate data. 57 | 50. Rust's `std::iter::Chain` type provides an iterator that chains together two borrowed iterators, enabling data concatenation. 58 | 51. Rust's `std::iter::MultiPeek` type provides an iterator that allows multiple lookahead values, which can be useful for working with borrowed data in parsing or pattern matching algorithms. 59 | 52. Rust's `std::iter::Peekable` type provides an iterator that allows peeking at the next value without consuming it, which can be helpful when working with borrowed data in parsing or pattern matching algorithms. 60 | 53. Rust's `std::iter::Copied` type provides an iterator that automatically copies the elements of a borrowed iterator, enabling data transformation. 61 | 54. Rust's `std::iter::Cloned` type provides an iterator that automatically clones the elements of a borrowed iterator, enabling data transformation without consuming the original elements. 62 | 55. Rust's `std::iter::Cycle` type provides an iterator that repeatedly cycles through the elements of a borrowed iterator, enabling generation of repeating sequences from existing data. 63 | 56. Rust's `std::iter::Take` type provides an iterator that yields the first `n` elements of a borrowed iterator, enabling data truncation and processing. 64 | 57. Rust's `std::iter::Skip` type provides an iterator that skips the first `n` elements of a borrowed iterator, enabling data manipulation and processing. 65 | 58. Rust's `std::iter::Fuse` type provides an iterator that becomes permanently exhausted once the underlying iterator is exhausted, enabling handling of borrowed data that can be consumed only once. 66 | 59. Rust's `std::iter::Zip` type provides an iterator that combines two borrowed iterators into a single iterator of pairs, enabling handling of parallel data structures. 67 | 60. Rust's `std::iter::Enumerate` type provides an iterator that yields pairs of indices and elements from a borrowed iterator, enabling handling of indexed data structures. 68 | 61. Rust's `std::iter::once` function creates an iterator that yields a single borrowed element, enabling handling of single-item data structures. 69 | 62. Rust's `std::iter::once_with` function creates an iterator that yields a single borrowed element computed by a given closure, enabling handling of single-item data structures with lazy evaluation. 70 | 63. You can use the `std::marker::PhantomPinned` type to create self-referential structures that are safely pinned in memory. 71 | 64. Rust's `AsRef` and `AsMut` traits allow for easy and generic conversion between types and their reference counterparts. 72 | 65. The `std::borrow::ToOwned` trait enables flexible cloning of borrowed data when ownership is required, without relying on specific clone implementations. 73 | 66. When working with trait objects, you can use the `Box<dyn Trait>` or `&dyn Trait` types to enforce dynamic dispatch with or without borrowing, respectively. 74 | 67. Rust's `Copy` trait allows for types that can be safely copied without invoking their `Drop` implementation, which simplifies working with borrowed data. 75 | 68. The `std::sync::atomic` module provides atomic operations that work with shared references, enabling lock-free concurrent programming. 76 | 69. You can use the `std::rc::Weak` and `std::sync::Weak` types to create weak references that don't prevent the data from being dropped, enabling cyclic data structures.The `std::sync::Once` type can be used to initialize global data with a specified lifetime, ensuring thread-safe, one-time initialization. 77 | 70. Rust's `std::sync::Condvar` type provides a way to work with condition variables, allowing threads to safely wait for a specific condition to be met while borrowing data. 78 | 71. Rust's `std::sync::Once` type provides a way to execute a given function exactly once, even across multiple threads, while working with borrowed data, ensuring proper initialization and data consistency. 79 | 72. Rust's `std::sync::mpsc` module provides multiple-producer, single-consumer channels for message passing between threads while working with borrowed data, ensuring proper communication and synchronization. 80 | 73. Rust's `std::sync::Arc` type provides a thread-safe reference-counted pointer for sharing borrowed data across multiple threads, ensuring proper synchronization and data consistency. 81 | 74. Rust's `std::sync::RwLock` type provides a read-write lock for multiple readers and single writer access to borrowed data across multiple threads, ensuring proper synchronization and data consistency. 82 | 75. Rust's `std::sync::Mutex` type provides a mutual exclusion lock for single-writer access to borrowed data across multiple threads, ensuring proper synchronization and data consistency. 83 | 76. Rust's `std::sync::Condvar` type provides a condition variable for synchronized access to borrowed data across multiple threads, enabling coordination and signaling between threads. 84 | 77. Rust's `std::sync::Barrier` type provides a synchronization primitive for multiple threads to wait for each other to reach a certain point in their execution, ensuring proper coordination when working with borrowed data. 85 | 78. Rust's `std::sync::Mutex` and `std::sync::RwLock` types provide the `lock` and `try_lock` methods for acquiring a mutable reference to borrowed data, ensuring proper synchronization and preventing data races. 86 | 79. In Rust, you can use the `lazy_static` crate to create references to lazily-initialized global data with a specified lifetime. 87 | 80. Rust's `CoerceUnsized` trait allows for custom pointer types that can be automatically coerced to unsized types, such as dynamically-sized arrays or trait objects. 88 | 81. The `std::cell::UnsafeCell` type enables the creation of mutable memory locations that can be safely shared between threads, bypassing Rust's borrowing rules when used carefully. 89 | 82. The `by_ref` method can be used with iterators to create a mutable reference to the iterator, enabling further chaining of iterator methods. 90 | 83. Rust's `std::collections` module contains many data structures that provide access to stored values through references. 91 | 84. In Rust, you can use the `deref` and `deref_mut` methods to create custom reference types with their own dereference behavior, enabling more ergonomic access to underlying data. 92 | 85. When implementing `Drop` for your custom types, you can safely access borrowed data to perform cleanup tasks. 93 | 86. Rust's `std::ptr::NonNull` type provides an optimization hint for the compiler that a raw pointer is never null, which can improve the performance of reference-heavy code. 94 | 87. The `try_borrow` and `try_borrow_mut` methods in `RefCell` provide non-panicking alternatives to borrowing mutable data, enabling more graceful error handling. 95 | 88. In Rust, you can use the `matches!` macro to test for a specific pattern in a borrowed value, providing a more concise and readable way to perform pattern matching. 96 | 89. The `std::borrow::Cow` type can be used to store either a borrowed reference or an owned value, providing storage and access to data that may be either owned or borrowed. 97 | 90. Rust's `Extend` trait enables appending of elements to collections, supporting both owned values and borrowed references. 98 | 91. The `std::ffi` module provides utilities for working with foreign function interfaces (FFI), enabling safe interaction between Rust and other programming languages through borrowing and references. 99 | 92. Rust's `std::marker::PhantomData` type can be used to indicate that your custom type logically contains a value of another type, without actually storing it, which is useful when working with borrowed data. 100 | 93. Rust's `slice::Windows` and `slice::Chunks` types provide iterators for efficiently iterating over borrowed slices in windows or chunks, enabling safe and convenient data manipulation. 101 | 94. Rust's `slice::Iter` and `slice::IterMut` types provide iterators for iteration over borrowed slices, both mutable and immutable. 102 | 95. The `slice::concat` function can be used to concatenate a slice of slices into a single owned slice, enabling manipulation of borrowed data. 103 | 96. The `slice::binary_search` and `slice::binary_search_by` methods can be used with borrowed data to perform binary search based on the `Ord` trait or a custom comparison function. 104 | 97. The `slice::contains` and `slice::starts_with` methods can be used with borrowed slices to check for the presence of a sub-slice or if the slice starts with a given sub-slice, respectively, enabling data validation. 105 | 98. The `slice::rotate_left` and `slice::rotate_right` methods can be used to efficiently rotate a mutable borrowed slice in-place, enabling data manipulation. 106 | 99. The `slice::reverse` method can be used to efficiently reverse a mutable borrowed slice in-place, enabling data manipulation. 107 | 100. The `slice::iter` and `slice::iter_mut` methods can be used with borrowed slices to create iterators over shared or mutable references to elements, enabling data traversal. 108 | 101. The `slice::windows` method can be used with borrowed slices to create an iterator over overlapping windows of a specified size, enabling pattern matching and data processing. 109 | 102. The `slice::split` and `slice::split_mut` methods can be used with borrowed slices to create iterators over disjoint sub-slices, enabling data partitioning and processing. 110 | 103. The `slice::chunks` and `slice::chunks_mut` methods can be used with borrowed slices to create iterators over non-overlapping sub-slices of a specified size, enabling data partitioning and processing. 111 | 104. The `slice::sort`, `slice::sort_unstable` and `slice::sort_by` methods can be used to sort a mutable borrowed slice, providing in-place sorting based on the `Ord` trait or a custom comparison function. 112 | 105. The `slice::reverse` method can be used with mutable borrowed slices to efficiently reverse their elements in-place, enabling data manipulation. 113 | 106. The `PartialEq` and `PartialOrd` traits can be implemented for custom types to enable comparisons between borrowed and owned data. 114 | 107. Rust's `std::io` module provides I/O operations using borrowed data through the `Read` and `Write` traits. 115 | 108. The `std::fmt` module provides formatting traits, such as `Display` and `Debug`, that can work with both owned values and borrowed references, enabling custom display and debugging behavior. 116 | 109. Rust's `String::from_utf8_lossy` function can be used to create a `Cow<str>` from a borrowed slice of bytes, providing a way to convert byte data into a string. 117 | 110. Rust's `std::cmp::Ordering` enum allows for the comparison of values, including borrowed data, using the `PartialOrd` and `Ord` traits. 118 | 111. The `Iterator::collect` method can be used to efficiently transform a borrowed iterator into a collection, such as a `Vec`, `HashSet`, or `HashMap`. 119 | 112. The `Iterator::map` method can be used with borrowed iterators to create a new iterator that applies a function to each element, allowing for data transformation. 120 | 113. The `Iterator::count` method can be used with borrowed iterators to count the number of elements, without consuming the iterator. 121 | 114. The `Iterator::cloned` method can be used with borrowed iterators to create a new iterator that yields cloned elements, enabling data processing while retaining the original borrowed data. 122 | 115. Rust's `Iterator::take_while` and `Iterator::skip_while` methods can be used with borrowed iterators to create new iterators that yield elements based on a given predicate. 123 | 116. The `Iterator::any` and `Iterator::all` methods can be used with borrowed iterators to check if any or all elements satisfy a given predicate, enabling data validation. 124 | 117. The `Iterator::position` and `Iterator::rposition` methods can be used with borrowed iterators to find the index of the first or last element that satisfies a given predicate, respectively, enabling data search. 125 | 118. The `Iterator::min_by_key` and `Iterator::max_by_key` methods can be used with borrowed iterators to find the minimum and maximum elements based on a provided key function, enabling data processing. 126 | 119. The `Iterator::scan` method can be used with borrowed iterators to create a new iterator that yields a sequence of accumulated values, enabling data transformation. 127 | 120. The `Iterator::fold` method can be used with borrowed iterators to accumulate elements in a single value, enabling data reduction. 128 | 121. The `Iterator::enumerate` method can be used with borrowed iterators to create a new iterator that yields pairs of element indices and values, enabling data processing. 129 | 122. The `Iterator::sum` and `Iterator::product` methods can be used with borrowed iterators to compute the sum and product of elements, respectively, enabling data reduction. 130 | 123. The `Iterator::step_by` method can be used with borrowed iterators to create a new iterator that yields every nth element, enabling data sampling. 131 | 124. The `Iterator::find_map` method can be used with borrowed iterators to find the first element that satisfies a given predicate and maps it to a new value, enabling data search and transformation. 132 | 125. The `Iterator::min_by` and `Iterator::max_by` methods can be used with borrowed iterators to find the minimum and maximum elements based on a custom comparison function, enabling data processing. 133 | 126. The `Iterator::nth` method can be used with borrowed iterators to efficiently access the nth element without consuming the iterator, enabling data access. 134 | 127. Rust's `Iterator::zip` method can be used to create a new iterator that yields pairs of elements from two borrowed iterators, enabling combining of data. 135 | 128. The `Iterator::unzip` method can be used with borrowed iterators to efficiently transform an iterator of pairs into two separate iterators, enabling data separation. 136 | 129. The `Iterator::filter` method can be used with borrowed iterators to create a new iterator that only yields elements that satisfy a given predicate, enabling filtering of data. 137 | 130. The `Iterator::map_while` method can be used with borrowed iterators to create a new iterator that applies a function to elements while a given condition holds, enabling data transformation. 138 | 131. The `Iterator::filter_map` method can be used with borrowed iterators to create a new iterator that applies a function to elements and yields only the resulting `Some` values, enabling data filtering and transformation. 139 | 132. The `Iterator::flat_map` method can be used with borrowed iterators to create a new iterator that applies a function to elements and flattens the resulting nested iterators, enabling data transformation. 140 | 133. The `Iterator::count` method can be used with borrowed iterators to efficiently count the number of elements, enabling data analysis. 141 | 134. The `Iterator::flatten` method can be used with borrowed iterators of nested iterators to create a new iterator that flattens the nested structure, enabling data transformation. 142 | 135. The `Iterator::inspect` method can be used with borrowed iterators to create a new iterator that applies a function to elements for side effects, enabling data processing and debugging. 143 | 136. The `Iterator::min` and `Iterator::max` methods can be used with borrowed iterators to efficiently find the minimum and maximum elements, enabling data analysis. 144 | 137. The `Iterator::take_while` method can be used with borrowed iterators to create a new iterator that yields elements while a given condition holds, enabling data filtering. 145 | 138. The `Iterator::skip_while` method can be used with borrowed iterators to create a new iterator that skips elements while a given condition holds, enabling data filtering. 146 | 139. The `Iterator::position` and `Iterator::rposition` methods can be used with borrowed iterators to efficiently find the index of the first or last element satisfying a given condition, enabling data analysis. 147 | 140. The `Iterator::by_ref` method can be used with borrowed iterators to create a new iterator that borrows from the original iterator, enabling data processing while retaining access to the original iterator. 148 | 141. The `std::cmp::min` and `std::cmp::max` functions can be used with borrowed data to find the minimum and maximum values, respectively, based on the `Ord` trait. 149 | 142. The `std::cell::Ref` and `std::cell::RefMut` types provide safe, dynamically-checked borrowing of data inside `RefCell`, ensuring proper lifetime management. 150 | 143. Rust's `std::ops::Range` type represents a range of values, which can be used with borrowed data to perform slicing or iteration. 151 | 144. Rust's `std::ops::Deref` and `std::ops::DerefMut` traits allow you to define custom reference types with their own dereference behavior, enabling more ergonomic access to underlying data. 152 | 145. The `std::ops::RangeInclusive` type represents an inclusive range of values, which can be used with borrowed data for slicing or iteration. 153 | 146. Rust's `std::ops::Index` and `std::ops::IndexMut` traits can be implemented for custom types to provide indexing of borrowed data. They allow you to define custom indexing behavior for your types with reference-based access. 154 | 147. Rust's `std::ops::RangeBounds` trait can be implemented for custom types to provide range-based access to borrowed data. 155 | 148. Rust's `std::ops::Add` and `std::ops::Sub` traits can be implemented for custom types to enable addition and subtraction of borrowed data. 156 | 149. Rust's `std::ops::Mul` and `std::ops::Div` traits can be implemented for custom types to enable multiplication and division of borrowed data. 157 | 150. Rust's `std::ops::Neg` and `std::ops::Not` traits can be implemented for custom types to enable negation and logical negation of borrowed data, respectively. 158 | 151. Rust's `std::ops::Rem` trait can be implemented for custom types to enable modulo operation with borrowed data. 159 | 152. Rust's `std::ops::Shl`, `std::ops::Shr`, `std::ops::BitAnd`, `std::ops::BitOr`, and `std::ops::BitXor` traits can be implemented for custom types to enable bitwise operations with borrowed data. 160 | 153. Rust's `std::ops::Fn`, `std::ops::FnMut`, and `std::ops::FnOnce` traits can be implemented for custom types to enable function-like behavior with borrowed data. 161 | 154. Rust's `std::ops::AddAssign`, `std::ops::SubAssign`, `std::ops::MulAssign`, `std::ops::DivAssign`, and `std::ops::RemAssign` traits can be implemented for custom types to enable compound assignment operations with borrowed data. 162 | 155. Rust's `std::ops::Deref` and `std::ops::DerefMut` traits can be implemented for custom types to enable access to borrowed data through custom reference types. 163 | 156. Rust's `std::ops::Drop` trait can be implemented for custom types to enable custom behavior when borrowed data is dropped, ensuring proper resource management. (Danger Will Robinson) 164 | 157. Rust's `std::ops::RangeBounds` trait can be implemented for custom types to enable indexing of borrowed data using custom range bounds. 165 | 158. Rust's `std::ops::CoerceUnsized` trait can be implemented for custom types to enable coercion between borrowed data of different sizes, such as slices or trait objects. 166 | 159. Rust's `std::ops::AsRef` and `std::ops::AsMut` traits can be implemented for custom types to enable conversion of custom reference types to borrowed data. 167 | 160. Rust's `std::ops::Try` trait can be implemented for custom types to enable error handling with borrowed data. 168 | 161. Rust's `std::ops::Neg` trait can be implemented for custom types to enable negation of borrowed data. 169 | 162. Rust's `std::ops::From` and `std::ops::Into` traits can be implemented for custom types to enable conversion between borrowed data and custom reference types. 170 | 164. Rust's `std::ops::TryFrom` and `std::ops::TryInto` traits can be implemented for custom types to enable fallible conversion between borrowed data and custom reference types. 171 | 165. Rust's `std::ops::AsRef` and `std::ops::AsMut` traits can be implemented for custom types to enable conversion of custom reference types to borrowed data references. 172 | 166. Rust's `std::ops` module contains traits, such as `Index` and `IndexMut`, that Rust's `std::str::Chars` type provides an iterator over grapheme clusters in a borrowed string slice, enabling Unicode text processing. 173 | 167. Rust's `std::ops::CoerceUnsized` and `std::ops::Unsize` traits can be implemented for custom types to enable coercion between custom reference types and dynamically sized borrowed data. 174 | 168. The `std::str::Chars` and `std::str::CharIndices` types provide iterators over characters and their indices in a string slice, respectively, enabling string manipulation. 175 | 169. Rust's `std::str::pattern` module offers various search algorithms that operate on borrowed string data, allowing for text searching and manipulation. 176 | 170. Rust's `std::str::Lines` type provides an iterator over lines in a borrowed string slice, enabling text processing. 177 | 171. Rust's `std::str::split_whitespace` method provides an iterator over words in a borrowed string slice, enabling text processing. 178 | 172. Rust's `std::str::from_utf8_unchecked` function can be used to convert a borrowed byte slice into a UTF-8 string slice without validating the data, but it should be used with caution, as invalid data can lead to undefined behavior. 179 | 173. Rust's `std::str::SplitN` and `std::str::RSplitN` types provide iterators over substrings in a borrowed string slice, limited to a specific number of substrings, enabling text processing. 180 | 174. The `std::str::repeat` method can be used to create a new string by repeating a borrowed string slice a specified number of times. 181 | 175. Rust's `std::str::Matcher` trait provides an interface for string pattern matching, which can be implemented for custom types that work with borrowed string data. 182 | 176. Rust's `std::str::parse` method can be used to parse a borrowed string slice into a value of a specified type, providing string-to-value conversion. 183 | 177. The `std::str::Chars` iterator provides the `as_str` method, which returns a borrowed string slice of the remaining characters, enabling processing of partial string data. 184 | 178. The `std::str::trim`, `std::str::trim_start`, and `std::str::trim_end` methods can be used with borrowed string slices to remove leading and trailing whitespace or specified characters, enabling text processing. 185 | 179. The `std::str::to_ascii_lowercase` and `std::str::to_ascii_uppercase` methods can be used with borrowed string slices to create new strings with all ASCII characters converted to lowercase or uppercase, respectively, enabling text processing. 186 | 180. The `std::str::replace` method can be used with borrowed string slices to create a new string with all occurrences of a specified pattern replaced with another string, enabling text manipulation. 187 | 181. The `std::str::escape_default` method can be used with borrowed string slices to create a new string with special characters escaped using the Rust escape syntax, enabling text representation and processing. 188 | 182. The `std::str::matches` method can be used with borrowed string slices to create an iterator over non-overlapping occurrences of a specified pattern, enabling pattern matching and text processing. 189 | 183. The `std::str::contains` method can be used with borrowed string slices to efficiently check whether a specified pattern is present in the text, enabling pattern matching. 190 | 184. The `std::str::starts_with` and `std::str::ends_with` methods can be used with borrowed string slices to efficiently check whether the text starts or ends with a specified pattern, enabling pattern matching. 191 | 185. The `std::str::trim`, `std::str::trim_start`, and `std::str::trim_end` methods can be used with borrowed string slices to efficiently remove leading and/or trailing whitespace, enabling text manipulation. 192 | 186. The `std::str::replace` method can be used with borrowed string slices to efficiently replace all occurrences of a specified pattern with another pattern, enabling text manipulation. 193 | 187. The `std::str::split` and `std::str::splitn` methods can be used with borrowed string slices to efficiently break the text into parts based on a specified delimiter, enabling text manipulation. 194 | 188. The `std::str::lines` method can be used with borrowed string slices to efficiently create an iterator over lines of text, enabling text manipulation. 195 | 189. Rust's `std::str::from_utf8` function can be used to safely convert a borrowed byte slice into a UTF-8 string slice, ensuring that the resulting string is valid. 196 | 190. Rust's `std::path::Path` and `std::path::PathBuf` types provide a safe way to work with filesystem paths, handling borrowed and owned data transparently. 197 | 191. Rust's `std::path::Components` and `std::path::Iter` types provide iterators for working with the components of filesystem paths, handling borrowed and owned data transparently. 198 | 192. Rust's `std::path::StripPrefixError` type provides a way to handle errors that occur when trying to strip a prefix from a borrowed path, ensuring proper error handling and data consistency. 199 | 193. Rust's `std::path::Path::ancestors` method provides an iterator over borrowed path components. 200 | 194. Rust's `std::path::Path::parent` method provides a way to work with borrowed path components. 201 | 195. Rust's `std::path::Path::file_name` method provides a way to work with borrowed file name components,. 202 | 196. Rust's `std::path::Path::extension` method provides a way to work with borrowed file extension components, enabling manipulation of filesystem paths. 203 | 197. Rust's `std::path::Path::join` method provides a way to work with borrowed path components, enabling concatenation and manipulation of filesystem paths. 204 | 198. Rust's `std::path::Path::is_absolute` and `std::path::Path::is_relative` methods provide a way to work with borrowed path components, enabling determination of whether a path is absolute or relative. 205 | 199. Rust's `std::path::Path::components` method provides a way to work with borrowed path components, enabling traversal and manipulation of filesystem paths. 206 | 200. Rust's `std::fmt::Arguments` type provides a way to work with formatted string arguments, both borrowed and owned, enabling custom formatting behavior. 207 | 201. Rust's `std::ffi::OsStr` and `std::ffi::OsString` types provide a safe way to work with platform-specific strings, handling borrowed and owned data transparently. 208 | -------------------------------------------------------------------------------- /PopularRustCrates.txt: -------------------------------------------------------------------------------- 1 | A few hundred popular and commonly used Crates from among the 120,000+ Rust crates.... 2 | 3 | By Dragos Ruiu 4 | Jul 06 2023 for Robust Rust 5 | 6 | actix: Actix is a Rust actor system and web framework that's extremely fast. It's a simple and efficient framework for building concurrent applications in Rust. 7 | actix-rt: Actix-rt is a runtime system for Actix actors. This library provides everything required to run an Actix application including the system executor, timing facilities, and an I/O reactor. 8 | actix-service: Actix-service provides an abstraction for composable asynchronous services. It's designed to be used with Actix but can be used independently as well. 9 | actix-web: Actix-web is a powerful, pragmatic, and extremely fast web framework for Rust, built on the actix actor framework. 10 | ahash: AHash is a high-speed keyed hashing algorithm intended for use in in-memory hashmaps. 11 | aho-corasick: Aho-Corasick is a fast library for searching multiple patterns at once, with support for regular expressions, with SIMD acceleration in some cases. 12 | amethyst: Amethyst is a game engine aiming to be fast and as configurable as possible. Its design is based on the Entity Component System (ECS) pattern for high performance and parallelizable code. 13 | ansi_term: Ansi_term is a library for controlling colors and formatting, such as red bold text or blue underlined text, on ANSI terminals. 14 | anyhow: Anyhow is a crate for flexible error handling in Rust. It provides an easy way to create, manage, and return errors and is often used in applications and libraries that don't need to define their own error types. It provides the anyhow::Error type for easy error wrapping and propagation. 15 | approx: Approx is a library for approximate comparison of floating point types. It provides macros for comparing floating point numbers with a specified tolerance. 16 | async-std: Async-std is a library that provides asynchronous counterparts to standard library types. It provides async versions of key std types like async-std::task for spawning tasks, or async-std::fs::File for file I/O, making async programming more ergonomic. 17 | async-trait: Async-trait is a library for making async trait methods easier. It transforms async functions to return boxed dyn Futures. It provides a way around the restriction of async functions not being allowed in traits by automatically boxing the futures that async functions return. 18 | backtrace: Backtrace is a library in Rust for collecting backtraces programmatically, useful for logging and error reporting. 19 | base64: Base64 crate is a set of routines that encode and decode base64 data. Base64 encoding is commonly used when there is a need to encode binary data, especially when that data needs to be stored or transferred over media designed to handle text. 20 | bevy: Bevy is a refreshingly simple data-driven game engine and app framework built in Rust. 21 | bincode: Bincode is a crate for encoding and decoding using a tiny binary serialization strategy. Using it, one can create and handle binary protocols and file formats, and you can easily go from having an object in memory, quickly serialize it to bytes, and then deserialize it back just as fast! 22 | brotli: Brotli is a lossless compression algorithm and format that provides better compression ratios than Gzip and Deflate. The brotli crate provides bindings and wrappers for this algorithm. 23 | bytemuck: Bytemuck is a crate for safe transmutes and data structure reinterpretation. 24 | byteorder: Byteorder is a utility that provides Read and Write traits with methods to read from and write to byte slices in either big-endian or little-endian order. 25 | bytes: Bytes is a utility library that provides efficient byte buffer structures, which are commonly used in asynchronous I/O operations, especially in network programming. 26 | byte-unit: Byte-unit is a library to represent sizes in different byte units like bytes, kilobytes, megabytes, and so on. 27 | capnp: Capnp provides Rust language bindings for the Cap'n Proto data interchange format, which is a type system for distributed systems. 28 | cargo: Cargo is the Rust package manager which downloads your Rust package's dependencies, compiles your packages, makes distributable packages, and uploads them to crates.io. 29 | cargo-edit: Cargo-edit adds subcommands to Cargo (the Rust package manager) for modifying Cargo.toml files from the command line. It's useful for adding, removing, and upgrading dependencies with ease. 30 | cc: The cc crate is a build-time dependency for Cargo build scripts to compile C/C++ code into a static archive to link against a Rust library. It makes integrating C/C++ code with your Rust project simpler. 31 | chrono: Chrono is a date and time library for Rust. It provides a simple and easy-to-use API for handling time, including parsing and formatting time strings. 32 | clap: Clap is a command line argument parser. It's full-featured, easy to use, and can automatically generate help and usage messages for your command line applications. 33 | clap-derive: Clap-derive provides custom derive for clap, making it even easier to parse command-line arguments by defining a struct that matches the desired command-line arguments. 34 | clippy: Clippy is a collection of lints to catch common mistakes and improve your Rust code. It provides suggestions and automatic fixes for many common Rust coding errors and inefficiencies. 35 | colored: Colored is a simple library to color terminal text output. It comes with multiple features including styling, disabling color output, and Windows 10 support. 36 | cookie: Cookie provides types for working with HTTP cookies. It supports parsing and serialization, and also provides a signed and private (encrypted, authenticated) cookie jars. 37 | crossbeam: Crossbeam is a crate providing tools for concurrent and parallel programming. It includes structures like channels, queues, and deques, as well as utilities for spawning and synchronizing tasks. 38 | crossterm: Crossterm is a library that allows you to build rich terminal and console user interfaces and handle input events on all major platforms. 39 | csv: Csv is a fast and flexible CSV reader and writer, supporting Serde, various delimiters, quoting rules, and headers. 40 | derive_more: Derive_more provides various derive macros to simplify your Rust structs and enums, including conversions to/from other standard types. 41 | diesel: Diesel is an ORM (Object-Relational Mapping) and query builder for Rust. It's designed to reduce the boilerplate for database interactions and improve runtime safety. It uses a pure-Rust frontend around a raw SQL/query builder backend. 42 | diesel-derive-enum: This crate works with Diesel to add simple enum support to your database, which is useful when you want more type safety than string-based representations offer. 43 | diesel_migrations: Diesel_migrations helps you deal with database schema migrations in a safe way, leveraging Diesel's type safety to avoid runtime errors due to schema mismatches. 44 | dirs: Dirs is a library to find various standard directories on the system, such as the home directory, cache directory, or configuration directory. 45 | dirs-next: Dirs-next is a successor to the dirs and dirs-sys libraries and provides functions to find standard directories on the system. 46 | dotenv: Dotenv is a crate that loads environment variables from a .env file, if it is available. This is useful in scenarios where you need to inject configuration into your application at runtime. 47 | druid: Druid is a data-first Rust-native UI design toolkit. It's aimed at providing high-quality, polished, platform-native user interfaces. 48 | elasticlunr-rs: Elasticlunr-rs is a lightweight full-text search engine in Rust for use in Javascript and WebAssembly. 49 | env_logger: Env_logger is a logger configured via an environment variable. It's particularly useful for adding logging to applications and libraries. It's one of the most common ways to add logging to a Rust application. 50 | evmap: Evmap provides an eventually consistent, concurrent, lock-free hash map implementation in Rust. 51 | exonum: Exonum is a extensible open-source framework for creating blockchain applications. 52 | failure: Failure is a system for managing and producing errors in Rust. It's designed to make it easier to manage errors in your Rust code. 53 | fern: Fern is a simple, efficient, and flexible logging library that provides a way to emit logs from Rust programs and control where they go and what they look like. 54 | flame: Flame is a simple hot code profiler for Rust. It's used for finding performance bottlenecks in Rust code. 55 | flate2: Flate2 is a crate providing bindings for zlib, miniz, or the Rust-flate library, which offers compression and decompression routines for deflate, gzip, and zlib formats. 56 | fnv: Fnv is a Rust library that provides Fowler/Noll/Vo hash (FNV hash), a simple hash function that is fast for short keys and has decent dispersion properties. 57 | futures: Futures is a library providing the foundations for asynchronous programming in Rust. It includes key trait definitions like Stream, Future, and IntoFuture. It provides tools to work with futures and streams, which are abstractions over asynchronous computations. 58 | futures_cpupool: Futures_cpupool is a library that creates a thread pool for executing CPU-bound tasks in Rust futures. It's useful for executing futures that are both I/O and CPU-bound. 59 | futures-timer: Futures-timer provides futures-based timers. It's useful when you want to use timers with futures and tokio runtime. 60 | futures-util: Futures-util contains the utility types and functions for the futures-rs library, such as combinators. 61 | getopts: Getopts is a command-line option parsing library, enabling you to extract options and their arguments from a list of strings. 62 | gfx: Gfx is a high-performance, bindless graphics API for the Rust programming language. It aims to be the default API for Rust game/platform-agnostic graphics. 63 | ggez: Ggez is a Rust library to create a Good Game Easily. It's a simple 2D game framework inspired by Love2D. 64 | gif: Gif provides support for encoding and decoding GIF images and animations in Rust. 65 | glium: Glium is an alternative to raw OpenGL that aims to provide an API that is efficient, safe, and easy to use. 66 | glob: Glob is a library for finding files using Unix shell style pattern matching. It's useful for operations that involve file systems and searching files. 67 | globset: Globset allows you to compile multiple globs into a single GlobSet that you can match against a path. 68 | glue: Glue is an async runtime for Rust with a focus on speed, correctness, and ergonomic design. 69 | glutin: Glutin is a cross-platform library for handling OpenGL context creation, event handling, and window manipulation. 70 | goblin: Goblin is a cross-platform, zero-copy, and macro-free binary parsing crate. It supports parsing ELF, Mach-O, and PE binaries with a unified interface. 71 | h2: H2 is a pure-Rust implementation of the HTTP/2.0 protocol. It provides a high-level API for managing HTTP/2.0 connections, streams, and frames. 72 | half: Half provides half-precision floating point types in Rust including f16 and bf16 for computation and machine learning uses. 73 | handlebars-rust: Handlebars-rust is a Rust templating library based on the Handlebars language, which is a superset of Mustache. It's useful for web development and generating code. 74 | hashbrown: Hashbrown provides a Rust-native implementation of a hash table, based on the Google SwissTable design. 75 | heck: Heck provides a library for converting names between different casing conventions like CamelCase, snake_case, kebab-case, and SHOUTY_SNAKE_CASE. 76 | hex: Hex is a simple library that provides functions for encoding and decoding data into and from hexadecimal. 77 | html5ever: Html5ever is a high-quality HTML5 parser that can be used standalone or as part of Servo, the parallel browser engine project. 78 | http: The http crate provides types for working with HTTP. It provides type safety over raw strings for many concepts used in HTTP. 79 | humansize: Humansize provides functionality to convert filesize units into human-readable strings. 80 | hyper: Hyper is a fast and correct HTTP/1.1 and HTTP/2 implementation written in and for Rust. Hyper provides both a Client and a Server. It's based on Tokio and is often used when you need fine-grained control over HTTP requests and responses. 81 | hyper-native-tls: Hyper-native-tls is a native TLS connector for the hyper library, an HTTP library in Rust. 82 | hyper-tls: Hyper-tls provides SSL/TLS support for the Hyper library via the native-tls crate. It enables HTTPS support in Hyper, a popular HTTP library for Rust. 83 | image: Image is a library that provides basic imaging processing functions and allows you to manipulate images in a variety of formats, including loading, saving, transforming, and drawing images. 84 | imageproc: Imageproc is a library for image processing. It's built on top of the image crate and provides various algorithms and operations you can apply to images. 85 | im: Im is a library that provides immutable data structures for Rust. 86 | indexmap: Indexmap is a hash table where the iteration order of the keys is independent of the hash values of the keys. 87 | indicatif: Indicatif is a library for creating progress bars and spinners in command-line interfaces. It helps make long-running command-line applications more informative and visually appealing. 88 | indoc: Indoc is a Rust library that provides a macro to produce indented strings, which is useful when working with multi-line string literals. 89 | input_buffer: Input_buffer provides a simple and efficient way to manage user input in console applications. 90 | instant: Instant is a library to get the current time in a platform-independent way. It helps in measuring time elapsed. 91 | iron: Iron is a high level web framework built in and for Rust, built on hyper for HTTP handling, inspired by Rack and Express.js. 92 | itertools: Itertools is a library for more iterator functionality. It adds extra functionality to Rust's iterators, including new adaptors, methods, and macros. 93 | jpeg-decoder: Jpeg-decoder is a library for decoding JPEG images into a pixel buffer. 94 | jsonwebtoken: Jsonwebtoken provides JSON Web Token support (JWT). It can be used to encode, decode, sign and verify JWT tokens. 95 | juniper: Juniper is a Rust library for building GraphQL servers that are type-safe and fast. It makes it easy to write GraphQL servers in Rust and provides a solid foundation for building complex APIs. 96 | jwalk: Jwalk is a library for a fast, parallel directory walking. It's especially suited for use cases that need to process a large number of files. 97 | kube: Kube is a Kubernetes client for Rust. It enables Rust applications to interact with Kubernetes clusters. 98 | kuchiki: Kuchiki is an HTML/XML tree manipulation library inspired by the JavaScript library jQuery. 99 | lalrpop: Lalrpop is a Rust library for building LR(1) parsers. It's useful for writing compilers, interpreters, or any tooling where you need to parse a custom language or file format. Its main advantage is its excellent error reporting. 100 | lapin: Lapin is an AMQP (Advanced Message Queuing Protocol) client library, allowing asynchronous communication between services. 101 | lasso: Lasso provides string interning, which is a method of storing only one copy of each distinct string value, which must be immutable. 102 | lazy_static: Lazy_static allows you to create statics that require one-time, potentially expensive, computations. Useful for initializing complex objects. Using this library, you can have statics that require code to be run at runtime in order to be initialized. 103 | libc: Libc provides all of the definitions necessary to easily interoperate with C code (or "C-like" code), in a platform-agnostic way. It includes types and function headers from the C standard library. 104 | log: Log is a flexible logging library for Rust. It provides a standard logging API that other libraries can use to implement logging, and applications can then choose how they want to display or handle logs. 105 | maplit: Maplit provides a set of macros for easily creating collections with literal syntax, such as hashmap! 106 | maud: Maud is a fast, safe, and easy-to-use template engine for Rust that allows you to write HTML templates in pure Rust, using compile-time generation to keep your runtime overhead low. 107 | md-5: MD-5 is an implementation of the MD5 hash function. It's a widely used cryptographic hash function that produces a 128-bit (16-byte) hash value. 108 | mdbook: Mdbook is a utility to create modern online books from Markdown files, similar to Gitbook but implemented in Rust. 109 | mime: Mime is a library for handling MIME types. It's necessary when handling HTTP to describe the type of the content. 110 | miniz_oxide: Miniz_oxide is a Rust rewrite of the miniz DEFLATE/zlib encoder/decoder. 111 | mio: Mio is a lightweight, low-level I/O library for Rust focusing on non-blocking APIs and event notification for building high performance I/O apps with as little overhead as possible over the OS. 112 | mockito: Mockito is a library for creating HTTP mocks for use in testing. 113 | nalgebra: Nalgebra is a linear algebra library for Rust, which supports mathematical operations on vectors, matrices, and quaternions featuring various matrix and vector types, including both statically and dynamically sized ones. 114 | native-tls: Native-tls is a crate providing a binding to the platform's native TLS stack, allowing for TLS connectivity in a platform-agnostic way. 115 | nix: Nix is a library for providing a friendly API to Unix system services (like sockets or process management) that are not covered by the Rust standard library. It provides a friendly, type-safe interface to system calls and other OS functionality that is easy to use and efficient. 116 | nom: Nom is a parser combinator library with a focus on safe parsing, streaming patterns, and as much as possible zero-copy. It's particularly useful for tasks such as parsing binary formats or text-based formats with complex rules. 117 | num_cpus: Num_cpus is a utility that determines the number of CPUs available on the current system. 118 | num-iter: Num-iter is a library that provides functions for producing an iterator over the range of a numeric primitive type. 119 | num: Num is a collection of numeric types and traits for Rust, including complex numbers, rationals, range iterators, generic integers, and random numbers. 120 | num-traits: Num-traits provides numeric traits for generic mathematics in Rust, which is useful when writing algorithms that can work with any kind of numbers. 121 | once_cell: Once_cell provides two new cell-like types, OnceCell and Lazy, that can store arbitrary non-Copy types and are initialized exactly once. 122 | openssl: Openssl provides bindings to the OpenSSL library for handling SSL and TLS network protocols, and for cryptography. 123 | parity-scale-codec: Parity-scale-codec provides a codec for encoding and decoding data in the SCALE (Simple Concatenated Aggregate Little-Endian) data format. 124 | parking_lot: Parking_lot offers faster and more flexible lock types, such as mutexes and read-write locks, improving over the standard library's synchronization primitives. 125 | parquet: Parquet is a columnar storage file format optimized for use with big data processing frameworks. The parquet crate provides read-write support for the Parquet format in Rust. 126 | percent-encoding: Percent-encoding provides functions to percent-encode and -decode strings according to RFC 3986, which is used for URL encoding and decoding. 127 | pest: Pest is a general purpose parser written in Rust with a focus on accessibility, correctness, and performance. It uses parsing expression grammars (or PEG) as input. 128 | phf: Phf provides compile-time–created maps and sets, similar to Python's frozenset. 129 | piston: Piston is a user-friendly game engine written in Rust, designed to make 2D game development easier and more modular. 130 | prettytable-rs: Prettytable-rs is a library to print aligned and formatted tables. It's highly customizable and allows to individually format cells, rows, columns or the entire table. 131 | proc-macro-hack: Proc-macro-hack is a library that enables the use of function-like procedural macros in item and statement positions (as of Rust 1.30). 132 | prost: Prost is a protocol buffers implementation in Rust. Protocol Buffers (a.k.a., protobuf) are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data. 133 | pulldown-cmark: Pulldown-cmark is a Rust library for parsing CommonMark, a fully specified variant of Markdown. 134 | quickcheck: Quickcheck is a way to run random tests on your code, inspired by the QuickCheck library for Haskell. It allows you to specify properties that your code should have, and then generates test cases to try and refute those properties. 135 | quick-error: Quick-error is a library for easily creating custom error types, greatly simplifying error handling. 136 | quote: Quote is a library for safely including literal Rust syntax into token streams. It's typically used for code generation. 137 | r2d2: R2D2 is a general-purpose connection pool written in Rust. It's useful for reducing the overhead of database connections. 138 | r2d2-postgres: R2d2-postgres provides Postgres support for the r2d2 connection pool. 139 | rand: Rand is a library for generating random numbers in Rust. It provides a simple and convenient API for generating random data for a variety of distributions. 140 | rand_chacha: Rand_chacha is a library that provides ChaCha random number generators for the Rand library. 141 | rand_distr: Rand_distr provides random number distributions to supplement the Rand crate, adding additional statistical distributions. 142 | rayon: Rayon is a data-parallelism library for Rust. It allows for easy and efficient execution of parallel tasks using work-stealing scheduling. 143 | regex: Regex is a Rust library for parsing, compiling, and executing regular expressions, offering functionality comparable to what you'd expect in Perl or Python, implemented as a safe wrapper around the PCRE library. 144 | reqwest: Reqwest is an easy and powerful HTTP request library. It features a convenient higher-level API for HTTP requests and handles many details for you, like redirection, compression, and cookies. It supports both synchronous and asynchronous operations, making it a go-to crate for any Rust application needing to make HTTP requests. 145 | reqwest-blocking: This is a synchronous HTTP client for the Rust programming language. It blocks on network requests, which can be useful in certain contexts where async is not needed or not possible. 146 | reqwest-middleware: Reqwest-middleware provides a middleware stack for Reqwest, allowing you to augment the behavior of your Reqwest client with custom middleware. 147 | rocket: Rocket is a web framework for Rust with a focus on ease of use, expressiveness, and speed. It's particularly well suited for building web applications thanks to its declarative syntax and powerful features. 148 | rocket_contrib: Rocket_contrib is a collection of useful, optional libraries for the Rocket web framework. It includes JSON and template support. 149 | ron: Ron (Rusty Object Notation) is a simple readable data serialization format that's easy to read, write, and maintain. It was designed to support all the common data types and data structures. 150 | rouille: Rouille is a micro web framework that's easy to comprehend and simple to get started with, providing just the essentials for building web applications or services. 151 | rusoto: Rusoto is an AWS SDK for Rust. It provides a Rusty interface for AWS services, making it easier to interact with them in a Rust-idiomatic way. 152 | rusqlite: Rusqlite is a set of Rust bindings for the SQLite database. 153 | rustache: Rustache is a Rust port of Mustache, a logic-less template system. 154 | rustbox: RustBox is a Rust implementation of TermBox, which is a simple and clean ncurses alternative for building text-based user interfaces. 155 | rust-crypto: Rust-crypto is a (mostly) pure-Rust implementation of various common cryptographic algorithms. 156 | rust-ini: Rust-ini is a straightforward library for ini file reading and writing in Rust. 157 | rustls: Rustls is a modern TLS library written in Rust. It's designed to provide a safe and performance-focused alternative to OpenSSL. 158 | rustorm: Rustorm is an SQL-centered ORM (Object-Relational Mapping) that focuses on mapping rows into rust structs, making database manipulation more intuitive. 159 | rust-s3: Rust-s3 is a Rust library for working with Amazon S3 or any S3-compatible services. 160 | scopeguard: Scopeguard provides macros for executing code at the end of a scope, helpful for ensuring cleanup is always performed, even if early returns or panics happen. 161 | scraper: Scraper is an HTML parsing and querying library inspired by BeautifulSoup, built on top of html5ever and cssselect. 162 | select-rs: Select-rs is a Rust library for extracting useful data from HTML documents, suitable for web scraping. 163 | semver: Semver is a Rust library for parsing, manipulating, and comparing semantic versions and version requirements. 164 | serde: Serde is a framework for serializing and deserializing Rust data structures efficiently and generically. It's one of the most widely used crates in Rust because of its versatility and high performance. 165 | serde_derive: Serde_derive provides a derive macro for the Serialize and Deserialize traits of the Serde serialization framework. 166 | serde_json: Serde_json is a crate for serializing/deserializing data to and from JSON using the serde serialization framework. 167 | serde_urlencoded: Serde_urlencoded provides a way to serialize and deserialize application/x-www-form-urlencoded data using the Serde framework, useful when working with HTTP and handling URL-encoded data. 168 | serde_yaml: Serde_yaml provides a way to serialize and deserialize YAML data using the Serde framework, useful when working with YAML configuration files. 169 | shiplift: Shiplift is a multi-transport Docker API client, useful for programmatically controlling Docker containers. 170 | shred: Shred is a library for managing and accessing system resources and components in a concurrency-friendly and data-oriented way. It is used by the Amethyst game engine for resource management. 171 | slog: Slog is a structured, composable logging for Rust. It provides a way to maintain detailed logs in production, for libraries to add context to log records, and for flexible processing and output of log data. 172 | specs: Specs is an Entity-Component System written in Rust. It's used for highly parallel game development and other simulation tasks. 173 | spin: Spin provides a simple spinlock implementation, useful in scenarios where std::sync::Mutex cannot be used. 174 | structopt: StructOpt combines clap with custom derive to make command-line argument parsing even easier. It allows you to define a struct that matches the desired command line arguments, reducing boilerplate code. 175 | strum: Strum is a set of macros and traits to work with enums and strings easier in Rust. 176 | syn: Syn is a parsing library for parsing a stream of Rust tokens into a syntax tree of Rust source code. It's often used in conjunction with the quote and proc_macro2 libraries to create procedural macros. 177 | tantivy: Tantivy is a full-text search engine library inspired by Apache Lucene and written in Rust. 178 | tempdir: Tempdir is a library to create a temporary directory and delete its entire contents when it goes out of scope. 179 | tempfile: Tempfile is a library for creating temporary files and directories. It provides secure, unique, and cleanup-on-drop temp files and directories. 180 | tera: Tera is a template engine inspired by Jinja2 and the Django template language. It's useful for generating text from a template and some variables. 181 | termcolor: Termcolor is a simple cross-platform library to deal with terminal colors. It provides a unified way to get access to terminal capabilities, including colors, from various platforms. 182 | textwrap: Textwrap is a library for word wrapping text. It can be used to format strings into paragraphs. 183 | thiserror: Thiserror is a library for making error handling more ergonomic by providing a derive macro for generating implementations of std::error::Error. 184 | tide: Tide is a minimal, pragmatic Rust web application framework built for rapid development. It comes with a robust set of features that make building database-backed web applications easier and more fun. 185 | tikv-client: Tikv-client is a Rust client for TiKV, a distributed transactional key-value database developed by PingCAP. 186 | time: Time is a simple and easy-to-use date and time library in Rust. It provides user-friendly ways to get the current time, parse and format dates, and compute duration. 187 | tokio: Tokio is an asynchronous runtime for Rust, focusing on networking, I/O, timers, and more designed for building high-performance networking applications with async and await. It's a key player in the async ecosystem and is often used in combination with async-std. 188 | tokio-postgres: Tokio-postgres is a non-blocking, async PostgreSQL driver for Rust using Tokio. It allows for efficient use of PostgreSQL database with Rust's async I/O ecosystem. 189 | tokio-rustls: Tokio-rustls provides async support for rustls, a modern TLS library written in Rust. It's commonly used for adding TLS support in Tokio-based networking applications. 190 | tokio-threadpool: Tokio-threadpool is a library providing a fast multiproducer, multisum consumer, work stealing, asynchronous, concurrent queue for building task schedulers. It's a part of the Tokio platform. 191 | tokio-tungstenite: Tokio-tungstenite provides Tokio-compatible bindings to the Tungstenite WebSocket library, allowing you to build asynchronous WebSocket clients and servers. 192 | toml: The toml crate provides functionality for parsing and serializing TOML documents in Rust. 193 | tonic: Tonic is a gRPC over HTTP/2 implementation focused on high performance, interoperability, and flexibility, and builds on top of Tokio and Hyper. 194 | tower: Tower is a library for writing robust networking clients and servers in Rust. It provides reusable components for handling concerns like load balancing, timeouts, retries, and more. 195 | tracing: Tracing is a framework for instrumenting Rust programs to collect structured, event-based diagnostic information. It's particularly useful in async programming and helps in debugging and understanding program execution. 196 | tui-rs: Tui-rs is a library for building rich terminal user interfaces and dashboards, with capabilities for drawing tables, sparklines, progress bars and more. 197 | tungstenite: Tungstenite provides a pure Rust implementation of the WebSocket (RFC6455) protocol. It enables client and server applications to support this protocol for communication. 198 | twox-hash: Twox-hash provides implementations of the xxHash32 and xxHash64 hash algorithms, which are designed to be fast and produce few collisions. 199 | typed-arena: Typed-arena is a fast, but limited, allocator. It is particularly well-suited for implementing graphs and other complex data structures that reference themselves. 200 | typenum: Typenum is a Rust library for type-level numbers evaluated at compile time. It currently supports bits, unsigned integers, and signed integers. 201 | unicode-segmentation: Unicode-segmentation is a library for iterating over Unicode strings according to the Unicode Standard Annex #29 rules. It allows for character-accurate string segmentation. 202 | unicode-width: Unicode-width provides functions to compute the width of characters and strings, given by the Unicode Standard Annex #11. 203 | ureq: Ureq is a minimal request library in Rust. It's a simple, safe, and convenient way to make HTTP requests and handle responses. 204 | url: Url is a crate for parsing and manipulating URLs, adhering to the URL Standard. This provides a set of tools to work with URLs in a robust and reliable way. 205 | uuid: This crate provides support for universally unique identifiers (UUIDs). It's useful for generating and parsing UUIDs, which are often used as database keys or for other unique identification purposes. 206 | vulkano: Vulkano is a safe wrapper around the Vulkan graphics API. It provides an interface that is strongly typed, making illegal states unrepresentable. 207 | walkdir: Walkdir is a crate that provides an efficient and cross-platform way to recursively walk a directory. It's particularly useful for applications that need to interact with the file system. It's built with iterators and options to custom control the behavior of the traversal. 208 | warp: Warp is a web server framework built on top of hyper, focusing on ergonomics, safety, and speed. It uses a filter system that makes route handling highly composable and easy to manage. Warp uses the Tokio stack and hyper for HTTP handling. 209 | wasm-bindgen: Wasm-bindgen facilitates high-level interactions between Rust and JavaScript. It allows you to work with JavaScript objects in Rust, and call JavaScript APIs and functions from Rust. 210 | wasm-bindgen-futures: Wasm-bindgen-futures provides integration between Rust Futures and JavaScript Promises. This is especially useful for Rust programs targeting WebAssembly. 211 | wasm-pack: Wasm-pack is a tool for assembling and packaging Rust code into WebAssembly, making it easier to use Rust in web environments. 212 | whoami: Whoami is a simple library that provides information about the user and system running the program. It's useful for programs that need to know the current user or system's information. 213 | winapi: Winapi is a direct mapping of Windows API bindings for Rust. It provides raw FFI bindings to all of Windows API. 214 | winreg: Winreg is a Rust interface to Windows Registry API. It provides an ergonomic way of accessing and manipulating Windows registry keys and values. 215 | x11-dl: X11-dl is a library for dynamically loading X11 libraries at runtime. This is useful for optional X11 support in a cross-platform application. 216 | x11rb: X11rb is a library to interact with the X11 protocol for writing X11 clients. It provides an idiomatic, safe, and performant way to create graphical applications targeting the X11 windowing system. 217 | xdg: Xdg is a library for managing XDG Base Directories in Unix-like operating systems. It assists in managing configuration, data, and cache directories in a standardized and organized way. 218 | yaml-rust: Yaml-rust is a pure Rust YAML 1.2 implementation, which allows Rust programs to read and write YAML files. 219 | yew: Yew is a modern Rust framework for creating multi-threaded front-end web apps with WebAssembly. It features a component-based architecture similar to React and Elm. 220 | zeroize: Zeroize is a library for securely zeroing memory, even for optimized builds where the compiler would otherwise optimize away zeroing operations. 221 | zip: Zip provides read and write support for the ZIP archive file format, including compression. 222 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RobustRust 2 | Secure Rust Code 3 | -------------------------------------------------------------------------------- /RustSyntacticSugarList.md: -------------------------------------------------------------------------------- 1 | Rust Syntactic Sugar 2 | 3 | (Robust Rust) 4 | 5 | Dragos Ruiu (2023 May 1) 6 | 7 | Here's a list of shorthand notations, shortcuts, and other syntactically unusual aspects of Rust, along with explanations and code examples: 8 | 9 | 10 | 11 | 1. Field Init Shorthand: `Point { x, y }` instead of `Point { x: x, y: y }`. 12 | 2. Destructuring: `let Point { x, y } = point;` or `let (a, b, c) = tuple;`. 13 | 3. Match Arms with Identical Expressions: `Message::ChangeColor(0, 0, 0) | Message::Move { x: 0, y: 0 } => println!("Do nothing")`. 14 | 4. Method Chaining: `"hello world".to_string().to_uppercase()`. 15 | 5. Closure Shorthand: `|&x| x * x` instead of `|x: &i32| -> i32 { *x * *x }`. 16 | 6. `?` Operator: `File::open("file.txt")?` instead of explicit error handling with `match`. 17 | 7. Turbofish Operator: `x.parse::()` when type inference is not possible. 18 | 8. Underscores in Numeric Literals: `1_000_000` for better readability. 19 | 9. Type Aliases: `type Kilometers = i32;` for code clarity. 20 | 10. Associated Constants: `const PI: f64` inside a trait or struct. 21 | 11. Inclusive Range: `1..=5` includes both 1 and 5. 22 | 12. Range Expression: `0..5` for loop iteration or slicing. 23 | 13. Lazy Static: `lazy_static! { static ref VAR: Type = value; }` for lazily initialized static variables. 24 | 14. Module-level Constants: `const CONST_NAME: Type = value;`. 25 | 15. Wildcard Import: `use some_module::*;` to import all items from a module. 26 | 16. Lifetime Elision: Omitting lifetime parameters when the compiler can infer them. 27 | 17. Immutable References: `&T` for shared, immutable references. 28 | 18. Mutable References: `&mut T` for exclusive, mutable references. 29 | 19. Reference Operator: `&` for creating a reference to a value. 30 | 20. Dereference Operator: `*` for accessing the value behind a reference. 31 | 21. Ownership Transfer: `let x = y;` moves ownership of `y` to `x`. 32 | 22. Borrowing: `&y` to create a shared reference without transferring ownership. 33 | 31. Impl Trait: `fn return_iterator() -> impl Iterator` for returning an anonymous type that implements a specific trait. 34 | 32. Extern Crate: `extern crate my_crate;` for including external crates in Rust 2015 edition (not required in Rust 2018 edition and later). 35 | 33. Macro Invocation: `println!("Hello, world!");` invoking a macro with a `!` at the end. 36 | 34. Attribute Macros: `#[derive(Debug)]` for automatically implementing traits or other code-generation tasks. 37 | 35. Module Declaration: `mod my_module;` for declaring a module in a separate file. 38 | 36. Nested Module Declaration: `mod inner { ... }` for declaring a nested module within the same file. 39 | 37. Visibility Modifiers: `pub` keyword for making items public, and `pub(crate)` for making items visible only within the current crate. 40 | 38. Struct Update Syntax: `Point { x: 3, ..point }` for creating a new struct instance while reusing the values from an existing instance. 41 | 39. Or Patterns: `Some(0) | Some(1)` in pattern matching to match against multiple patterns. 42 | 40. If Let: `if let Some(x) = option { ... }` for concise single pattern matching. 43 | 41. While Let: `while let Some(x) = iterator.next() { ... }` for loop iteration with single pattern matching. 44 | 42. Function Pointer: `fn add(x: i32, y: i32) -> i32;` for specifying a function as a value. 45 | 43. AsRef and AsMut: `as_ref()` and `as_mut()` for converting a value into a reference or mutable reference of a different type. 46 | 44. Try Blocks: `try { ... }` for a block returning a `Result`, available in nightly Rust builds. 47 | 45. Associated Types: `type Output;` within a trait, for specifying a type that is associated with the implementing type. 48 | 46. Default Trait Implementations: `fn method(&self) -> Type { ... }` within a trait, for providing default implementations that can be overridden by implementers. 49 | 47. Trait Bounds: `T: Trait` in generic function signatures, for constraining the generic types to implement specific traits. 50 | 48. Higher-Ranked Trait Bounds: `for<'a> T: Trait<'a>` for constraining a generic type to implement a trait with an associated lifetime. 51 | 49. Lifetime Annotations: `'a` for explicitly specifying lifetimes of references in structs, functions, and traits. 52 | 50. Static Lifetimes: `'static` for references with the longest possible lifetime, living for the entire duration of the program. 53 | 51. Range Syntax: `a..b` for creating a range of values between `a` and `b`. 54 | 52. Inclusive Range Syntax: `a..=b` for creating an inclusive range, including both `a` and `b`. 55 | 53. Array and Slice Pattern: `[a, b, .., y, z]` for destructuring arrays or slices. 56 | 54. Field Init Shorthand: `Foo { a, b }` for initializing a struct's fields with the same name as the variables. 57 | 55. C-style Enum: `enum Enum { A, B, C }` for declaring a C-style enumeration. 58 | 56. Use Renaming: `use std::io::Result as IoResult;` for renaming an imported item. 59 | 57. Type Alias: `type MyResult = Result;` for creating an alias for a type. 60 | 58. Raw Pointers: `*const T` and `*mut T` for raw (unsafe) pointers. 61 | 59. Repeat Expression: `[0; 5]` for creating an array with repeated values. 62 | 60. String Literal Concatenation: `"Hello, " "world!"` for concatenating adjacent string literals. 63 | 61. Unreachable Code: `unreachable!()` macro for marking a code location as unreachable. 64 | 63. Dereference Operator: `*x` for dereferencing a reference or pointer. 65 | 64. Raw Identifier: `r#type` for using reserved keywords as identifiers. 66 | 65. Loop Labels: `'label: loop { ... }` for labeling loops, and `break 'label;` or `continue 'label;` for breaking or continuing labeled loops. 67 | 66. Async Functions: `async fn foo() -> Result<(), ()> { ... }` for declaring asynchronous functions. 68 | 67. Pin: `std::pin::Pin` for creating a pinned reference, which ensures the memory it points to will not be moved. 69 | 68. Unsized Types: `T: ?Sized` trait bound for allowing generic types to be unsized. 70 | 69. Zero-sized types: `struct Zst;` for declaring a zero-sized type (ZST) that occupies no memory. 71 | 70. Constant Function: `const fn foo() { ... }` for declaring a function that can be evaluated at compile-time. 72 | 71. Associated Constants: `const VALUE: i32 = 42;` for defining associated constants within trait or struct implementations. 73 | 72. Or-patterns: `Some(0) | Some(1) | Some(2)` for matching multiple patterns in a single arm. 74 | 73. Anonymous Lifetime: `'_` for specifying an anonymous lifetime parameter. 75 | 74. Boxed trait objects: `Box` for creating a boxed trait object, which can hold any type that implements the trait. 76 | 75. Dereference coercion: Automatically dereferencing a reference to a reference, e.g., `&String` to `&str`. 77 | 76. Diverging type: `!` for specifying a diverging type, which indicates that the function does not return. 78 | 77. Module shorthand: `mod.rs` for defining a module in a separate file. 79 | 78. Eager macro expansion: `macro_rules! foo { ... }` for defining macros that are eagerly expanded during parsing. 80 | 79. Lazy macro expansion: `proc_macro! { ... }` for defining procedural macros that are expanded during code generation. 81 | 80. Type ascription: `let x: Type = expr;` for annotating the type of a variable explicitly. 82 | 81. Extern crate: `extern crate foo;` for explicitly linking an external crate. 83 | 82. NLL (Non-Lexical Lifetimes): More precise lifetime analysis, reducing restrictions on borrowing. 84 | 83. Field init shorthand: When initializing a struct, if a variable with the same name as a field is in scope, you can use the shorthand `fieldname` instead of `fieldname: fieldname`. 85 | 3. Byte string literals: `b"Hello, world!"` for creating a byte string literal (an array of `u8` values). 86 | 4. Raw string literals: `r#"..."#` for creating a raw string literal, which does not process escape characters. 87 | 5. Doc comments: `///` and `//!` for creating documentation comments that are processed by the Rust documentation tool (rustdoc). 88 | 6. Macro reexport: `#[macro_export]` for making a macro available to other crates when the module is used. 89 | 7. Global allocator: `#[global_allocator]` for specifying a custom global memory allocator for a Rust program. 90 | 8. Auto traits: Automatically implementing a trait for types that meet certain criteria, such as `Send` and `Sync`. 91 | 9. Scoped attributes: `#[attr]` for applying an attribute to an item in a limited scope. 92 | 10. Custom derive: `#[derive(MyTrait)]` for implementing custom derive macros that generate trait implementations. 93 | 11. Nested imports: `use std::{fs, io};` for importing multiple items from the same path in a single `use` statement. 94 | 13. Extern types: `extern type Foo;` for declaring an opaque type with an unknown size and alignment. 95 | 14. Binding modes: `ref` and `ref mut` for binding to a reference or mutable reference in pattern matching. 96 | 1. Try blocks: `try { ... }` for using the `?` operator within the block and propagating errors to the enclosing scope (experimental feature). 97 | 2. Negative implementations: `impl !Trait for Type` for specifying that a given type does not implement a particular trait. 98 | 3. Array concatenation: `[a; N]` for creating an array of length `N` with all elements set to `a`. 99 | 4. Array repeating: `[a; N]` for creating an array of length `N` with all elements set to `a`. 100 | 5. Scoped lint attributes: `#[allow(clippy::lint_name)]` for allowing or disallowing specific lints within a limited scope. 101 | 6. Lazy static: `lazy_static! { ... }` for declaring lazily initialized static values (provided by the `lazy_static` crate). 102 | 7. Associated consts: `const NAME: Type = value;` for declaring associated constants within a trait or implementation block. 103 | 8. Existential types: `existential type Foo: Trait;` for declaring a type alias for an opaque type that implements a given trait (experimental feature). 104 | 9. Struct update syntax: `..base` for creating a new struct with some fields updated from an existing struct. 105 | 10. Or patterns: `A | B` for matching on multiple patterns in a single arm of a `match` or `if let` expression. 106 | 11. Const generics: `struct Foo { ... }` for defining generic parameters with constant values (experimental feature). 107 | 12. Inline attributes: `#[inline]` and `#[inline(always)]` for suggesting that a function be inlined by the compiler. 108 | 1. Trait objects: `Box` for creating a trait object, which is a dynamically dispatched, heap-allocated object implementing a specific trait. 109 | 2. Automatic dereferencing: Rust automatically dereferences references and smart pointers when calling methods, so you don't need to explicitly dereference them using `*`. 110 | 3. Range patterns: `a..=b` for matching a range of values inclusively within a `match` expression. 111 | 4. Nested import groups: `use foo::{bar, baz::{qux, quux}};` for importing multiple items from the same module in a more concise way. 112 | 5. Enum variant aliases: `type Alias = Enum::Variant;` for creating a type alias for a specific enum variant. 113 | 6. Destructuring assignment: `let (a, b) = (1, 2);` for unpacking a tuple or struct into individual variables. 114 | 7. "turbofish" for const generics: `foo::<{ value }>()` for specifying const generic parameters (similar to the turbofish operator for type generics). 115 | 8. Nested functions: Rust allows you to define functions inside other functions, which can help with organizing code and encapsulating logic. 116 | 1. Visibility shortcut: `pub(crate)` or `pub(super)` can be used to specify the visibility of an item within a crate or its parent module, respectively. 117 | 2. Inline assembly: Rust provides support for inline assembly through the `asm!` macro, which enables you to include assembly language code directly in your Rust program for highly optimized or low-level operations. 118 | 3. "or patterns": You can match multiple patterns in a single arm of a `match` expression using the `|` operator, e.g., `Some(x) | Some(y)` matches `Some(x)` or `Some(y)`. 119 | 4. Non-exhaustive enums: You can mark an enum as non-exhaustive with the `#[non_exhaustive]` attribute. This signals to users of the API that the enum may have additional variants in the future, so they should include a catch-all `_` match arm when pattern matching. 120 | 5. Pinning: The `Pin` type is used to express that the memory address of a value should not change, which can be useful when working with self-referential types or certain async contexts. 121 | 6. Async closure: The `async` keyword can be used with closures to create asynchronous closures, although they are currently unstable and available only in nightly Rust. 122 | 1. Type-level integers: Rust supports type-level integers through the `typenum` crate, which allows you to create types with integers as type parameters. This can enable type-level programming techniques and compile-time computations. 123 | 2. Wrapping arithmetic: Rust provides the `Wrapping` type, which performs arithmetic operations that "wrap" on overflow or underflow, useful in certain low-level or cryptographic applications. 124 | 3. Uninhabited types: Rust has a special type called `!`, which represents an uninhabited type. It is used to indicate that a function never returns, such as a function that calls `std::process::exit` or enters an infinite loop. 125 | 4. Multidispatch: Rust supports limited multiple dispatch through traits and trait objects. This allows for more dynamic method resolution at runtime, depending on the actual types of the involved objects. 126 | 5. Custom dereference coercions: Implementing the `Deref` and `DerefMut` traits allows you to create custom dereference coercions for your types, effectively providing a way to overload the `*` operator. 127 | 1. Safe transmutes: The `safe-transmute` crate allows you to safely transmute one type to another, ensuring that the conversion is valid and well-defined, unlike the unsafe `std::mem::transmute` function. 128 | 2. Const generics: As of Rust 1.51, const generics allow you to parameterize types over constant values, enabling more expressive and powerful generic programming techniques. 129 | 3. Associated consts: You can define associated constants in traits, which allow you to provide constant values that are associated with a trait implementation for a specific type. 130 | 4. Unsafe traits: You can mark a trait as `unsafe`, which indicates that implementing the trait requires adhering to certain invariants that the Rust compiler cannot enforce. This feature is useful for creating abstractions over low-level or unsafe operations. 131 | 1. Diverging functions: Functions that never return have a special return type `!`, which is called the "never" type. This can be useful in cases where the function panics or enters an infinite loop. 132 | 2. Function pointers: Rust supports function pointers, which allow you to store references to functions and use them as arguments or return values. The syntax for a function pointer is `fn(ArgumentTypes) -> ReturnType`. 133 | 3. Custom allocators: Rust allows you to use custom memory allocators instead of the default global allocator. To do this, you can implement the `std::alloc::GlobalAlloc` trait and use the `#[global_allocator]` attribute to specify your custom allocator. 134 | 4. Macros: Rust has a powerful macro system that allows you to create reusable chunks of code using a concise syntax. Macros in Rust use the `macro_rules!` keyword to define patterns and expansions. 135 | 5. Custom derive: Rust allows you to implement custom derive macros, which can automatically generate trait implementations for your types based on the structure and fields of the type. 136 | 1. Attributes: Rust attributes are metadata applied to modules, items, or expressions. Attributes are denoted by a `#[]` or `#![]` syntax (the latter is used for inner attributes). Attributes can be used for various purposes, such as conditional compilation, customizing derives, or specifying test functions. 137 | 138 | Field Init Shorthand 139 | 140 | When initializing a struct, if the variable names match the field names, you can use the field init shorthand: 141 | 142 | 143 | ``` 144 | struct Point { 145 | x: i32, 146 | y: i32, 147 | } 148 | 149 | fn main() { 150 | let x = 3; 151 | let y = 4; 152 | 153 | let point = Point { x, y }; // Equivalent to `Point { x: x, y: y }` 154 | println!("({}, {})", point.x, point.y); 155 | } 156 | ``` 157 | 158 | 159 | Destructuring and Pattern Matching 160 | 161 | You can destructure structs, tuples, and enums using pattern matching: 162 | 163 | 164 | ``` 165 | struct Point { 166 | x: i32, 167 | y: i32, 168 | } 169 | 170 | fn main() { 171 | let point = Point { x: 3, y: 4 }; 172 | 173 | let Point { x, y } = point; 174 | println!("({}, {})", x, y); 175 | 176 | let tuple = (1, 2, 3); 177 | let (a, b, c) = tuple; 178 | println!("{}, {}, {}", a, b, c); 179 | } 180 | ``` 181 | 182 | 183 | Match Arms with Identical Expressions 184 | 185 | If multiple match arms have the same expression, you can use a pipe (`|`) to simplify the code: 186 | 187 | 188 | ``` 189 | enum Message { 190 | Quit, 191 | ChangeColor(u8, u8, u8), 192 | Move { x: i32, y: i32 }, 193 | } 194 | 195 | fn process_message(msg: Message) { 196 | match msg { 197 | Message::Quit => println!("Quit"), 198 | Message::ChangeColor(0, 0, 0) | Message::Move { x: 0, y: 0 } => println!("Do nothing"), 199 | _ => println!("Do something"), 200 | } 201 | } 202 | ``` 203 | 204 | 205 | Method Chaining 206 | 207 | You can chain methods using the dot (`.`) operator: 208 | 209 | 210 | ``` 211 | fn main() { 212 | let s = "hello world".to_string().to_uppercase(); 213 | println!("{}", s); 214 | } 215 | ``` 216 | 217 | 218 | Closure Shorthand 219 | 220 | When using closures, you can use the shorthand notation for parameters and their types: 221 | 222 | 223 | ``` 224 | fn main() { 225 | let numbers = vec![1, 2, 3, 4, 5]; 226 | let squares: Vec = numbers.iter().map(|&x| x * x).collect(); 227 | println!("{:?}", squares); 228 | } 229 | ``` 230 | 231 | 232 | 233 | 234 | ? Operator 235 | 236 | The `?` operator is a shorthand for error handling, returning the error early if a result is an `Err` variant: 237 | 238 | 239 | ``` 240 | use std::fs::File; 241 | 242 | fn main() -> Result<(), std::io::Error> { 243 | let file = File::open("file.txt")?; 244 | Ok(()) 245 | } 246 | ``` 247 | 248 | 249 | Turbofish Operator 250 | 251 | The turbofish operator (`::<>`) is used to specify generic type parameters when the type cannot be inferred: 252 | 253 | 254 | ``` 255 | fn main() { 256 | let nums = vec!["1", "2", "3"]; 257 | let nums: Vec = nums.into_iter().map(|x| x.parse::().unwrap()).collect(); 258 | println!("{:?}", nums); 259 | } 260 | ``` 261 | 262 | 263 | Underscores in Numeric Literals: 264 | 265 | 266 | ``` 267 | let million = 1_000_000; 268 | println!("One million is written as: {}", million); 269 | ``` 270 | 271 | 272 | Type Aliases: 273 | 274 | 275 | ``` 276 | type Kilometers = i32; 277 | 278 | fn distance_in_km(distance: Kilometers) { 279 | println!("The distance is {} kilometers", distance); 280 | } 281 | 282 | let km: Kilometers = 42; 283 | distance_in_km(km); 284 | ``` 285 | 286 | 287 | Associated Constants: 288 | 289 | 290 | ``` 291 | struct Circle { 292 | radius: f64, 293 | } 294 | 295 | impl Circle { 296 | const PI: f64 = 3.141592653589793; 297 | 298 | fn area(&self) -> f64 { 299 | self.radius * self.radius * Self::PI 300 | } 301 | } 302 | 303 | let circle = Circle { radius: 5.0 }; 304 | println!("The area of the circle is: {}", circle.area()); 305 | ``` 306 | 307 | 308 | Inclusive Range: 309 | 310 | 311 | ``` 312 | for i in 1..=5 { 313 | print!("{} ", i); 314 | } 315 | ``` 316 | 317 | 318 | Range Expression: 319 | 320 | 321 | ``` 322 | for i in 0..5 { 323 | print!("{} ", i); 324 | } 325 | 326 | let arr = [1, 2, 3, 4, 5]; 327 | let slice = &arr[1..4]; 328 | ``` 329 | 330 | 331 | Lazy Static: 332 | 333 | 334 | ``` 335 | use lazy_static::lazy_static; 336 | use std::collections::HashMap; 337 | 338 | lazy_static! { 339 | static ref FRUITS: HashMap<&'static str, u32> = { 340 | let mut map = HashMap::new(); 341 | map.insert("apple", 5); 342 | map.insert("banana", 3); 343 | map.insert("orange", 2); 344 | map 345 | }; 346 | } 347 | 348 | fn main() { 349 | let count = FRUITS.get("apple").unwrap(); 350 | println!("There are {} apples", count); 351 | } 352 | 353 | count); } 354 | ``` 355 | 356 | 357 | Module-level Constants: 358 | 359 | 360 | ``` 361 | const MAX_SIZE: usize = 100; 362 | 363 | fn main() { 364 | let mut array = [0; MAX_SIZE]; 365 | array[0] = 42; 366 | println!("The first element of the array is: {}", array[0]); 367 | } 368 | ``` 369 | 370 | 371 | Wildcard Import: 372 | 373 | 374 | ``` 375 | mod some_module { 376 | pub fn function_one() { 377 | println!("Function one"); 378 | } 379 | 380 | pub fn function_two() { 381 | println!("Function two"); 382 | } 383 | } 384 | 385 | use some_module::*; 386 | 387 | fn main() { 388 | function_one(); 389 | function_two(); 390 | } 391 | ``` 392 | 393 | 394 | Lifetime Elision: 395 | 396 | 397 | ``` 398 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { 399 | if x.len() > y.len() { 400 | x 401 | } else { 402 | y 403 | } 404 | } 405 | 406 | fn main() { 407 | let s1 = String::from("short"); 408 | let s2 = String::from("longer"); 409 | 410 | let res = longest(&s1, &s2); 411 | println!("The longest string is: {}", res); 412 | } 413 | ``` 414 | 415 | 416 | Immutable References: 417 | 418 | 419 | ``` 420 | fn print_length(s: &String) { 421 | println!("The length of the string is: {}", s.len()); 422 | } 423 | 424 | fn main() { 425 | let s = String::from("Hello, world!"); 426 | print_length(&s); 427 | } 428 | ``` 429 | 430 | 431 | Mutable References: 432 | 433 | 434 | ``` 435 | fn change(s: &mut String) { 436 | s.push_str(", world!"); 437 | } 438 | 439 | fn main() { 440 | let mut s = String::from("Hello"); 441 | change(&mut s); 442 | println!("{}", s); 443 | } 444 | ``` 445 | 446 | 447 | Reference Operator: 448 | 449 | 450 | ``` 451 | fn main() { 452 | let x = 42; 453 | let y = &x; 454 | println!("The value of y is: {}", y); 455 | } 456 | ``` 457 | 458 | 459 | Dereference Operator: 460 | 461 | 462 | ``` 463 | fn main() { 464 | let x = 42; 465 | let y = &x; 466 | let z = *y; 467 | println!("The value of z is: {}", z); 468 | } 469 | 470 | } 471 | ``` 472 | 473 | 474 | Ownership Transfer: 475 | 476 | 477 | ``` 478 | fn main() { 479 | let x = String::from("hello"); 480 | let y = x; 481 | println!("The value of y is: {}", y); 482 | } 483 | ``` 484 | 485 | 486 | Borrowing: 487 | 488 | 489 | ``` 490 | fn print_length(s: &String) { 491 | println!("The length of the string is: {}", s.len()); 492 | } 493 | 494 | fn main() { 495 | let s = String::from("Hello, world!"); 496 | print_length(&s); 497 | } 498 | ``` 499 | 500 | 501 | Impl Trait: 502 | 503 | 504 | ``` 505 | fn evens() -> impl Iterator { 506 | (0..).step_by(2) 507 | } 508 | 509 | fn main() { 510 | let mut iterator = evens(); 511 | println!("First even number: {}", iterator.next().unwrap()); 512 | println!("Second even number: {}", iterator.next().unwrap()); 513 | println!("Third even number: {}", iterator.next().unwrap()); 514 | } 515 | ``` 516 | 517 | 518 | Extern Crate (only required in Rust 2015 edition): 519 | 520 | 521 | ``` 522 | // Cargo.toml 523 | [dependencies] 524 | rand = "0.8.3" 525 | 526 | // main.rs 527 | extern crate rand; 528 | 529 | use rand::Rng; 530 | 531 | fn main() { 532 | let random_number = rand::thread_rng().gen_range(1, 101); 533 | println!("Random number: {}", random_number); 534 | } 535 | ``` 536 | 537 | 538 | Macro Invocation: 539 | 540 | 541 | ``` 542 | fn main() { 543 | println!("Hello, world!"); 544 | } 545 | ``` 546 | 547 | 548 | Attribute Macros: 549 | 550 | 551 | ``` 552 | #[derive(Debug)] 553 | struct Point { 554 | x: i32, 555 | y: i32, 556 | } 557 | 558 | fn main() { 559 | let p = Point { x: 1, y: 2 }; 560 | println!("{:?}", p); 561 | } 562 | ``` 563 | 564 | 565 | Module Declaration: 566 | 567 | 568 | ``` 569 | // my_module.rs 570 | pub fn hello() { 571 | println!("Hello from my_module!"); 572 | } 573 | 574 | // main.rs 575 | mod my_module; 576 | 577 | fn main() { 578 | my_module::hello(); 579 | } 580 | ``` 581 | 582 | 583 | Nested Module Declaration: 584 | 585 | 586 | ``` 587 | mod outer { 588 | pub mod inner { 589 | pub fn hello() { 590 | println!("Hello from inner module!"); 591 | } 592 | } 593 | } 594 | 595 | fn main() { 596 | outer::inner::hello(); 597 | } 598 | ``` 599 | 600 | 601 | Visibility Modifiers: 602 | 603 | 604 | ``` 605 | mod outer { 606 | pub mod inner { 607 | pub fn hello() { 608 | println!("Hello from inner module!"); 609 | } 610 | } 611 | } 612 | 613 | fn main() { 614 | outer::inner::hello(); 615 | } 616 | ``` 617 | 618 | 619 | Struct Update Syntax: Create a new struct instance while reusing values from an existing instance. 620 | 621 | 622 | ``` 623 | struct Point { 624 | x: i32, 625 | y: i32, 626 | } 627 | 628 | fn main() { 629 | let point = Point { x: 1, y: 2 }; 630 | let new_point = Point { x: 3, ..point }; 631 | println!("New point: ({}, {})", new_point.x, new_point.y); 632 | } 633 | ``` 634 | 635 | 636 | Or Patterns: Match against multiple patterns in pattern matching. 637 | 638 | 639 | ``` 640 | fn main() { 641 | let value = Some(0); 642 | 643 | match value { 644 | Some(0) | Some(1) => println!("Matched 0 or 1"), 645 | _ => println!("Didn't match 0 or 1"), 646 | } 647 | } 648 | ``` 649 | 650 | 651 | If Let: Concise single pattern matching. 652 | 653 | 654 | ``` 655 | fn main() { 656 | let option = Some(42); 657 | 658 | if let Some(x) = option { 659 | println!("Got a value: {}", x); 660 | } 661 | } 662 | ``` 663 | 664 | 665 | While Let: Loop iteration with single pattern matching. 666 | 667 | 668 | ``` 669 | fn main() { 670 | let mut iterator = 0..3; 671 | 672 | while let Some(x) = iterator.next() { 673 | println!("Value: {}", x); 674 | } 675 | } 676 | ``` 677 | 678 | 679 | Function Pointer: Specify a function as a value. 680 | 681 | 682 | ``` 683 | fn add(x: i32, y: i32) -> i32 { 684 | x + y 685 | } 686 | 687 | fn main() { 688 | let f: fn(i32, i32) -> i32 = add; 689 | println!("Result: {}", f(1, 2)); 690 | } 691 | ``` 692 | 693 | 694 | AsRef and AsMut: Convert a value into a reference or mutable reference of a different type. 695 | 696 | 697 | ``` 698 | fn print_length>(s: T) { 699 | println!("Length: {}", s.as_ref().len()); 700 | } 701 | 702 | fn main() { 703 | let s = String::from("hello"); 704 | print_length(s); 705 | } 706 | ``` 707 | 708 | 709 | Try Blocks: A block returning a Result, available in nightly Rust builds. 710 | 711 | 712 | ``` 713 | trait Iterator { 714 | type Item; 715 | 716 | fn next(&mut self) -> Option; 717 | } 718 | 719 | // Implementation of Iterator for a custom struct 720 | ``` 721 | 722 | 723 | Associated Types: Specify a type that is associated with the implementing type within a trait. 724 | 725 | 726 | ``` 727 | trait Iterator { 728 | type Item; 729 | 730 | fn next(&mut self) -> Option; 731 | } 732 | 733 | // Implementation of Iterator for a custom struct 734 | ``` 735 | 736 | 737 | Default Trait Implementations: Provide default implementations that can be overridden by implementers within a trait. 738 | 739 | 740 | ``` 741 | trait MyTrait { 742 | fn method(&self) -> &str { 743 | "Default implementation" 744 | } 745 | } 746 | 747 | struct MyStruct; 748 | 749 | impl MyTrait for MyStruct {} 750 | 751 | fn main() { 752 | let my_struct = MyStruct; 753 | println!("{}", my_struct.method()); // Uses the default implementation 754 | } 755 | ``` 756 | 757 | 758 | Trait Bounds: Constrain generic types to implement specific traits in generic function signatures. 759 | 760 | 761 | ``` 762 | fn print_length>(s: T) { 763 | println!("Length: {}", s.as_ref().len()); 764 | } 765 | 766 | fn main() { 767 | let s = String::from("hello"); 768 | print_length(s); 769 | } 770 | ``` 771 | 772 | 773 | Higher-Ranked Trait Bounds: Constrain a generic type to implement a trait with an associated lifetime. 774 | 775 | 776 | ``` 777 | trait Callable<'a> { 778 | fn call(&self, arg: &'a str); 779 | } 780 | 781 | fn call_twice(f: T, arg: &str) 782 | where 783 | for<'a> T: Callable<'a>, 784 | { 785 | f.call(arg); 786 | f.call(arg); 787 | } 788 | 789 | struct Printer; 790 | 791 | impl<'a> Callable<'a> for Printer { 792 | fn call(&self, arg: &'a str) { 793 | println!("{}", arg); 794 | } 795 | } 796 | 797 | fn main() { 798 | let printer = Printer; 799 | call_twice(printer, "Hello"); 800 | } 801 | ``` 802 | 803 | 804 | Lifetime Annotations: Explicitly specify lifetimes of references in structs, functions, and traits. 805 | 806 | 807 | ``` 808 | struct Foo<'a> { 809 | bar: &'a str, 810 | } 811 | 812 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { 813 | if x.len() > y.len() { 814 | x 815 | } else { 816 | y 817 | } 818 | } 819 | 820 | fn main() { 821 | let s1 = String::from("short"); 822 | let s2 = String::from("longer"); 823 | 824 | let result = longest(s1.as_str(), s2.as_str()); 825 | println!("Longest: {}", result); 826 | } 827 | ``` 828 | 829 | 830 | Static Lifetimes: References with the longest possible lifetime, living for the entire duration of the program. 831 | 832 | 833 | ``` 834 | static HELLO: &str = "Hello, world!"; 835 | 836 | fn main() { 837 | println!("{}", HELLO); 838 | } 839 | 840 | } 841 | ``` 842 | 843 | 844 | Range Syntax: Create a range of values between `a` and `b`. 845 | 846 | 847 | ``` 848 | fn main() { 849 | for i in 1..5 { 850 | println!("{}", i); 851 | } 852 | } 853 | ``` 854 | 855 | 856 | Inclusive Range Syntax: Create an inclusive range, including both `a` and `b`. 857 | 858 | 859 | ``` 860 | fn main() { 861 | for i in 1..=5 { 862 | println!("{}", i); 863 | } 864 | } 865 | 866 | } 867 | ``` 868 | 869 | 870 | Array and Slice Pattern: Destructure arrays or slices. 871 | 872 | 873 | ``` 874 | fn print_first_two_elements(slice: &[i32]) { 875 | if let [first, second, ..] = slice { 876 | println!("First two elements: {}, {}", first, second); 877 | } 878 | } 879 | 880 | fn main() { 881 | let numbers = [1, 2, 3, 4, 5]; 882 | print_first_two_elements(&numbers); 883 | } 884 | 885 | } 886 | ``` 887 | 888 | 889 | Field Init Shorthand: Initialize a struct's fields with the same name as the variables. 890 | 891 | 892 | ``` 893 | struct Point { 894 | x: i32, 895 | y: i32, 896 | } 897 | 898 | fn main() { 899 | let x = 3; 900 | let y = 4; 901 | let point = Point { x, y }; 902 | println!("Point: ({}, {})", point.x, point.y); 903 | } 904 | ``` 905 | 906 | 907 | C-style Enum: Declare a C-style enumeration. 908 | 909 | 910 | ``` 911 | enum Direction { 912 | Up, 913 | Down, 914 | Left, 915 | Right, 916 | } 917 | 918 | fn print_direction(direction: Direction) { 919 | match direction { 920 | Direction::Up => println!("Going up!"), 921 | Direction::Down => println!("Going down!"), 922 | Direction::Left => println!("Going left!"), 923 | Direction::Right => println!("Going right!"), 924 | } 925 | } 926 | 927 | fn main() { 928 | let direction = Direction::Up; 929 | print_direction(direction); 930 | } 931 | ``` 932 | 933 | 934 | Use Renaming: Rename an imported item. 935 | 936 | 937 | ``` 938 | use std::io::Result as IoResult; 939 | use std::fs::File; 940 | 941 | fn open_file(filename: &str) -> IoResult { 942 | File::open(filename) 943 | } 944 | 945 | fn main() { 946 | let result = open_file("file.txt"); 947 | match result { 948 | Ok(file) => println!("File opened successfully!"), 949 | Err(e) => println!("Failed to open the file: {}", e), 950 | } 951 | } 952 | ``` 953 | 954 | 955 | Type Alias: Provide a new name for an existing type, often for clarity or to shorten type names. 956 | 957 | 958 | ``` 959 | type Kilometers = i32; 960 | 961 | fn distance_in_miles(distance: Kilometers) -> f64 { 962 | const KILOMETERS_TO_MILES: f64 = 0.621371; 963 | f64::from(distance) * KILOMETERS_TO_MILES 964 | } 965 | 966 | fn main() { 967 | let distance: Kilometers = 100; 968 | println!("100 kilometers is {:.2} miles", distance_in_miles(distance)); 969 | } 970 | 971 | ``` 972 | 973 | 974 | `Raw Pointers: *const T and *mut T for raw (unsafe) pointers.` 975 | 976 | 977 | ``` 978 | fn main() { 979 | let x = 10; 980 | let y = &x as *const i32; 981 | unsafe { 982 | assert_eq!(*y, x); 983 | } 984 | } 985 | 986 | } } 987 | ``` 988 | 989 | 990 | Repeat Expression: `[0; 5]` for creating an array with repeated values. 991 | 992 | 993 | ``` 994 | fn main() { 995 | let a = [0; 5]; 996 | assert_eq!(a, [0, 0, 0, 0, 0]); 997 | } 998 | ``` 999 | 1000 | 1001 | String Literal Concatenation: Concatenate adjacent string literals. 1002 | 1003 | 1004 | ``` 1005 | fn main() { 1006 | let s = "Hello, " "world!"; 1007 | assert_eq!(s, "Hello, world!"); 1008 | } 1009 | ``` 1010 | 1011 | 1012 | Unreachable Code: `unreachable!()` macro for marking a code location as unreachable. 1013 | 1014 | 1015 | ``` 1016 | fn process_data(data: Option<&str>) { 1017 | match data { 1018 | Some(value) => println!("Processing data: {}", value), 1019 | None => unreachable!(), 1020 | } 1021 | } 1022 | 1023 | fn main() { 1024 | process_data(Some("Hello, world!")); 1025 | } 1026 | ``` 1027 | 1028 | 1029 | Underscore in Numeric Literals: Improve readability in large numeric literals. 1030 | 1031 | 1032 | ``` 1033 | fn main() { 1034 | let million = 1_000_000; 1035 | println!("One million is written as: {}", million); 1036 | } 1037 | 1038 | million); } 1039 | ``` 1040 | 1041 | 1042 | Dereference Operator: `*x` for dereferencing a reference or pointer. 1043 | 1044 | 1045 | ``` 1046 | fn main() { 1047 | let x = 10; 1048 | let y = &x; 1049 | assert_eq!(*y, x); 1050 | } 1051 | ``` 1052 | 1053 | 1054 | Raw Identifier: `r#type` for using reserved keywords as identifiers. 1055 | 1056 | 1057 | ``` 1058 | fn main() { 1059 | let r#type = "Reserved keyword as an identifier"; 1060 | println!("{}", r#type); 1061 | } 1062 | ``` 1063 | 1064 | 1065 | Loop Labels: `'label: loop { ... }` for labeling loops, and `break 'label;` or `continue 'label;` for breaking or continuing labeled loops. 1066 | 1067 | 1068 | ``` 1069 | fn main() { 1070 | 'outer: for x in 1..5 { 1071 | 'inner: for y in 1..5 { 1072 | if x * y == 6 { 1073 | println!("Found a match: {} * {} = 6", x, y); 1074 | break 'outer; 1075 | } 1076 | } 1077 | } 1078 | } 1079 | ``` 1080 | 1081 | 1082 | Async Functions: `async fn foo() -> Result<(), ()> { ... }` for declaring asynchronous functions. 1083 | 1084 | 1085 | ``` 1086 | use std::time::Duration; 1087 | use async_std::task; 1088 | 1089 | async fn async_sleep(seconds: u64) { 1090 | task::sleep(Duration::from_secs(seconds)).await; 1091 | } 1092 | 1093 | async fn example() { 1094 | println!("Start"); 1095 | async_sleep(2).await; 1096 | println!("Finish after 2 seconds"); 1097 | } 1098 | 1099 | fn main() { 1100 | let fut = example(); 1101 | async_std::task::block_on(fut); 1102 | } 1103 | 1104 | ``` 1105 | 1106 | 1107 | 1108 | Pin: `std::pin::Pin` for creating a pinned reference, which ensures the memory it points to will not be moved. 1109 | 1110 | 1111 | ``` 1112 | use std::pin::Pin; 1113 | 1114 | fn main() { 1115 | let x = 5; 1116 | let pinned = Pin::new(&x); 1117 | println!("Pinned value: {}", *pinned); 1118 | } 1119 | ``` 1120 | 1121 | 1122 | Unsized Types: `T: ?Sized` trait bound for allowing generic types to be unsized. 1123 | 1124 | 1125 | ``` 1126 | fn print_value(value: &T) { 1127 | println!("Value: {}", value); 1128 | } 1129 | 1130 | fn main() { 1131 | let value: &dyn std::fmt::Display = &42; 1132 | print_value(value); 1133 | } 1134 | ``` 1135 | 1136 | 1137 | Zero-sized types: `struct Zst;` for declaring a zero-sized type (ZST) that occupies no memory. 1138 | 1139 | 1140 | ``` 1141 | struct Zst; 1142 | 1143 | fn main() { 1144 | let _zst = Zst; 1145 | println!("Size of Zst: {}", std::mem::size_of::()); 1146 | } 1147 | 1148 | std::mem::size_of::()); } 1149 | ``` 1150 | 1151 | 1152 | Constant Function: `const fn foo() { ... }` for declaring a function that can be evaluated at compile-time. 1153 | 1154 | 1155 | ``` 1156 | rconst fn square(x: i32) -> i32 { 1157 | x * x 1158 | } 1159 | 1160 | const VAL: i32 = square(4); 1161 | 1162 | fn main() { 1163 | println!("The square of 4 is: {}", VAL); 1164 | ``` 1165 | 1166 | 1167 | } 1168 | 1169 | Associated Constants: `const VALUE: i32 = 42;` for defining associated constants within trait or struct implementations. 1170 | 1171 | 1172 | ``` 1173 | trait HasValue { 1174 | const VALUE: i32; 1175 | } 1176 | 1177 | struct MyStruct; 1178 | 1179 | impl HasValue for MyStruct { 1180 | const VALUE: i32 = 42; 1181 | } 1182 | 1183 | fn main() { 1184 | println!("MyStruct value: {}", MyStruct::VALUE); 1185 | } 1186 | 1187 | t::VALUE); } 1188 | ``` 1189 | 1190 | 1191 | Or-patterns: `Some(0) | Some(1) | Some(2)` for matching multiple patterns in a single arm. 1192 | 1193 | 1194 | ``` 1195 | fn match_small_numbers(n: Option) { 1196 | match n { 1197 | Some(0) | Some(1) | Some(2) => println!("Small number"), 1198 | _ => println!("Not a small number"), 1199 | } 1200 | } 1201 | 1202 | fn main() { 1203 | match_small_numbers(Some(1)); 1204 | match_small_numbers(Some(5)); 1205 | } 1206 | ``` 1207 | 1208 | 1209 | Anonymous Lifetime: `'_` for specifying an anonymous lifetime parameter. 1210 | 1211 | 1212 | ``` 1213 | fn foo(s: &'_ str) -> &'_ str { 1214 | &s[0..5] 1215 | } 1216 | 1217 | fn main() { 1218 | let s = String::from("Hello, world!"); 1219 | let result = foo(&s); 1220 | println!("{}", result); 1221 | } 1222 | ``` 1223 | 1224 | 1225 | Boxed trait objects: `Box` for creating a boxed trait object, which can hold any type that implements the trait. 1226 | 1227 | 1228 | ``` 1229 | trait Animal { 1230 | fn speak(&self); 1231 | } 1232 | 1233 | struct Dog; 1234 | struct Cat; 1235 | 1236 | impl Animal for Dog { 1237 | fn speak(&self) { 1238 | println!("Woof!"); 1239 | } 1240 | } 1241 | 1242 | impl Animal for Cat { 1243 | fn speak(&self) { 1244 | println!("Meow!"); 1245 | } 1246 | } 1247 | 1248 | fn make_animal_speak(animal: Box) { 1249 | animal.speak(); 1250 | } 1251 | 1252 | fn main() { 1253 | let dog: Box = Box::new(Dog); 1254 | let cat: Box = Box::new(Cat); 1255 | 1256 | make_animal_speak(dog); 1257 | make_animal_speak(cat); 1258 | } 1259 | ``` 1260 | 1261 | 1262 | Dereference coercion: Automatically dereferencing a reference to a reference, e.g., &String to &str. 1263 | 1264 | 1265 | ``` 1266 | fn print_str(s: &str) { 1267 | println!("{}", s); 1268 | } 1269 | 1270 | fn main() { 1271 | let s = String::from("Hello, world!"); 1272 | print_str(&s); 1273 | } 1274 | ``` 1275 | 1276 | 1277 | Diverging type: `!` for specifying a diverging type, which indicates that the function does not return. 1278 | 1279 | 1280 | ``` 1281 | fn panic_now() -> ! { 1282 | panic!("This function never returns"); 1283 | } 1284 | 1285 | fn main() { 1286 | println!("This will panic..."); 1287 | panic_now(); 1288 | } 1289 | ``` 1290 | 1291 | 1292 | Module shorthand: `mod.rs` for defining a module in a separate file. 1293 | 1294 | 1295 | ``` 1296 | // src/my_module/mod.rs 1297 | pub fn say_hello() { 1298 | println!("Hello from my_module!"); 1299 | } 1300 | 1301 | // src/main.rs 1302 | mod my_module; 1303 | 1304 | fn main() { 1305 | my_module::say_hello(); 1306 | } 1307 | ``` 1308 | 1309 | 1310 | Eager macro expansion: `macro_rules! foo { ... }` for defining macros that are eagerly expanded during parsing. 1311 | 1312 | 1313 | ``` 1314 | macro_rules! print_sum { 1315 | ($a:expr, $b:expr) => { 1316 | println!("The sum is: {}", $a + $b); 1317 | }; 1318 | } 1319 | 1320 | fn main() { 1321 | print_sum!(3, 5); 1322 | } 1323 | ``` 1324 | 1325 | 1326 | Lazy macro expansion: `proc_macro! { ... }` for defining procedural macros that are expanded during code generation. Procedural macros are more complex and require a separate crate of type `proc-macro`. Here's an example of a simple procedural macro to derive a custom trait `HelloWorld`: 1327 | 1328 | 1329 | ``` 1330 | // hello_world_macro/Cargo.toml 1331 | [lib] 1332 | proc-macro = true 1333 | 1334 | // hello_world_macro/src/lib.rs 1335 | extern crate proc_macro; 1336 | 1337 | use proc_macro::TokenStream; 1338 | use quote::quote; 1339 | use syn::{parse_macro_input, DeriveInput}; 1340 | 1341 | #[proc_macro_derive(HelloWorld)] 1342 | pub fn hello_world_derive(input: TokenStream) -> TokenStream { 1343 | let input = parse_macro_input!(input as DeriveInput); 1344 | let ident = input.ident; 1345 | 1346 | let expanded = quote! { 1347 | impl HelloWorld for #ident { 1348 | fn hello_world() { 1349 | println!("Hello, world! My name is {}!", stringify!(#ident)); 1350 | } 1351 | } 1352 | }; 1353 | 1354 | TokenStream::from(expanded) 1355 | } 1356 | 1357 | // hello_world/Cargo.toml 1358 | [dependencies] 1359 | hello_world_macro = { path = "../hello_world_macro" } 1360 | 1361 | // hello_world/src/main.rs 1362 | use hello_world_macro::HelloWorld; 1363 | 1364 | #[derive(HelloWorld)] 1365 | struct MyStruct; 1366 | 1367 | fn main() { 1368 | MyStruct::hello_world(); 1369 | } 1370 | ``` 1371 | 1372 | 1373 | Type ascription: `let x: Type = expr;` for annotating the type of a variable explicitly. 1374 | 1375 | 1376 | ``` 1377 | fn main() { 1378 | let x: i32 = 5; 1379 | println!("x is {}", x); 1380 | } 1381 | ``` 1382 | 1383 | 1384 | Extern crate: `extern crate foo;` for explicitly linking an external crate (only required in Rust 2015 edition, not in Rust 2018 edition and later). 1385 | 1386 | 1387 | ``` 1388 | // Rust 2015 edition 1389 | // Cargo.toml 1390 | [dependencies] 1391 | rand = "0.8.5" 1392 | 1393 | // main.rs 1394 | extern crate rand; 1395 | 1396 | use rand::Rng; 1397 | 1398 | fn main() { 1399 | let num = rand::thread_rng().gen_range(1..=10); 1400 | println!("Random number: {}", num); 1401 | } 1402 | ``` 1403 | 1404 | 1405 | NLL (Non-Lexical Lifetimes): More precise lifetime analysis, reducing restrictions on borrowing. 1406 | 1407 | Non-Lexical Lifetimes (NLL) is an enhancement to Rust's borrow checker that allows for more precise and flexible lifetime analysis. In Rust, lifetimes are used to ensure that references to memory are always valid. Before NLL, Rust's borrow checker used a more conservative approach based on lexical scopes, which sometimes resulted in false positives, making it harder to write certain code patterns. 1408 | 1409 | NLL improves the lifetime analysis by allowing the borrow checker to better understand when a reference is no longer used, even if it's still in scope. This can help reduce unnecessary restrictions on borrowing and make the code more ergonomic. 1410 | 1411 | Here's an example to illustrate the difference between lexical lifetimes and non-lexical lifetimes: 1412 | 1413 | 1414 | ``` 1415 | fn main() { 1416 | let mut x = 5; 1417 | let y = &x; 1418 | 1419 | 1420 | x = 6; // Error: cannot assign to `x` because it is borrowed 1421 | println!("{}", y); 1422 | } 1423 | ``` 1424 | 1425 | 1426 | In this example, the borrow checker with lexical lifetimes would produce an error because `x` is borrowed by `y` and cannot be modified. However, with NLL, the borrow checker can recognize that the reference `y` is not used after the assignment to `x`, and therefore, it's safe to modify `x`. 1427 | 1428 | Here's another example where NLL makes a difference: 1429 | 1430 | 1431 | ``` 1432 | fn process(val: &i32) { 1433 | // Do something with val 1434 | } 1435 | 1436 | fn main() { 1437 | let mut data = vec![1, 2, 3, 4, 5]; 1438 | 1439 | let item; 1440 | { 1441 | let first = &data[0]; 1442 | process(first); 1443 | item = first; 1444 | } 1445 | 1446 | data.push(6); // Error with lexical lifetimes, OK with NLL 1447 | } 1448 | 1449 | NLL } 1450 | ``` 1451 | 1452 | 1453 | In this example, the reference `first` is borrowed from `data` and processed, then assigned to `item`. With lexical lifetimes, the borrow checker would consider the borrow of `data` to be active until the end of the inner scope, which would make the `data.push(6)` call invalid. However, with NLL, the borrow checker can recognize that `first` is not used after it's assigned to `item`, and therefore, the `data.push(6)` call is safe and allowed. 1454 | 1455 | Non-Lexical Lifetimes is a significant improvement to Rust's borrow checker that allows for more precise lifetime analysis, making the code more ergonomic and flexible without compromising safety. 1456 | 1457 | Field init shorthand: 1458 | 1459 | 1460 | ``` 1461 | struct Point { 1462 | x: i32, 1463 | y: i32, 1464 | } 1465 | 1466 | fn main() { 1467 | let x = 3; 1468 | let y = 5; 1469 | let point = Point { x, y }; // Shorthand for Point { x: x, y: y } 1470 | } 1471 | ``` 1472 | 1473 | 1474 | Field init shorthand allows you to concisely initialize a struct when variables with the same names as the fields are in scope. 1475 | 1476 | Inclusive Ranges: 1477 | 1478 | 1479 | ``` 1480 | for i in 1..=3 { 1481 | println!("{}", i); 1482 | } 1483 | ``` 1484 | 1485 | 1486 | Inclusive Ranges create a range that includes both the start and end values, in this case, it will print 1, 2, and 3. 1487 | 1488 | Byte string literals: 1489 | 1490 | 1491 | ``` 1492 | let byte_string = b"Hello, world!"; 1493 | ``` 1494 | 1495 | 1496 | Byte string literals create an array of `u8` values, where each character in the string is represented by its ASCII value. 1497 | 1498 | Raw string literals: 1499 | 1500 | 1501 | ``` 1502 | let raw_string = r#"This is a "raw" string with "quotes" and \slashes\."#; 1503 | ``` 1504 | 1505 | 1506 | Raw string literals allow you to create strings that do not process escape characters, making it easier to include quotes and backslashes. 1507 | 1508 | Doc comments: 1509 | 1510 | 1511 | ``` 1512 | /// This function adds two numbers. 1513 | /// 1514 | /// # Examples 1515 | /// 1516 | /// ``` 1517 | /// let result = my_function(2, 3); 1518 | /// assert_eq!(result, 5); 1519 | /// ``` 1520 | pub fn my_function(a: i32, b: i32) -> i32 { 1521 | a + b 1522 | } 1523 | ``` 1524 | 1525 | 1526 | Doc comments are used to create documentation for functions, structs, and other items in your code, which are then processed by the Rust documentation tool `rustdoc`. 1527 | 1528 | Macro reexport: 1529 | 1530 | 1531 | ``` 1532 | #[macro_export] 1533 | macro_rules! my_macro { 1534 | () => { 1535 | println!("This is my exported macro!"); 1536 | }; 1537 | } 1538 | ``` 1539 | 1540 | 1541 | Macro reexport makes a macro available to other crates when the module is used, allowing them to use the macro as if it was defined in their own code. 1542 | 1543 | Global allocator: 1544 | 1545 | 1546 | ``` 1547 | use std::alloc::System; 1548 | 1549 | #[global_allocator] 1550 | static GLOBAL: System = System; 1551 | 1552 | fn main() { 1553 | let v = vec![1, 2, 3]; 1554 | } 1555 | ``` 1556 | 1557 | 1558 | Global allocator attribute allows you to specify a custom global memory allocator for a Rust program, which can be useful for optimizing memory usage or for using custom allocators. 1559 | 1560 | Auto traits: 1561 | 1562 | 1563 | ``` 1564 | // Compiler automatically implements Send and Sync for MyStruct 1565 | struct MyStruct { 1566 | a: i32, 1567 | b: String, 1568 | } 1569 | ``` 1570 | 1571 | 1572 | Auto traits are traits that the Rust compiler automatically implements for types that meet certain criteria, such as `Send` and `Sync`. This can simplify code and reduce boilerplate. 1573 | 1574 | Scoped attributes: 1575 | 1576 | 1577 | ``` 1578 | fn main() { 1579 | #[cfg(target_os = "windows")] 1580 | { 1581 | println!("Running on Windows"); 1582 | } 1583 | 1584 | #[cfg(not(target_os = "windows"))] 1585 | { 1586 | println!("Not running on Windows"); 1587 | } 1588 | } 1589 | ``` 1590 | 1591 | 1592 | Scoped attributes apply an attribute to an item within a limited scope, making it easier to conditionally compile or execute code based on configuration. 1593 | 1594 | Custom derive: 1595 | 1596 | 1597 | ``` 1598 | // In a separate crate 1599 | use proc_macro::TokenStream; 1600 | 1601 | #[proc_macro_derive(MyTrait)] 1602 | pub fn my_trait_derive(input: TokenStream) -> TokenStream { 1603 | // Implement your custom derive macro here 1604 | } 1605 | 1606 | // In your main crate 1607 | #[derive(MyTrait)] 1608 | struct MyStruct { 1609 | a: i32, 1610 | b: String, 1611 | } 1612 | ``` 1613 | 1614 | 1615 | Custom derive allows you to implement macros that generate trait implementations for your structs or enums. 1616 | 1617 | Nested imports: 1618 | 1619 | 1620 | ``` 1621 | use std::{fs, io}; 1622 | 1623 | fn main() { 1624 | let mut file = fs::File::open("file.txt").unwrap(); 1625 | let mut buffer = String::new(); 1626 | io::Read::read_to_string(&mut file, &mut buffer).unwrap(); 1627 | } 1628 | ``` 1629 | 1630 | 1631 | Nested imports let you import multiple items from the same path in a single `use` statement, making imports more concise. 1632 | 1633 | Underscore in numeric literals: 1634 | 1635 | 1636 | ``` 1637 | fn main() { 1638 | let large_number = 1_000_000; 1639 | println!("One million: {}", large_number); 1640 | } 1641 | 1642 | er); } 1643 | ``` 1644 | 1645 | 1646 | Underscores in numeric literals improve readability by visually separating digits in large numbers. 1647 | 1648 | Extern types: 1649 | 1650 | 1651 | ``` 1652 | extern "C" { 1653 | type Foo; 1654 | fn foo_new() -> *const Foo; 1655 | } 1656 | ``` 1657 | 1658 | 1659 | Extern types are used to declare an opaque type with an unknown size and alignment, often when interfacing with foreign code. 1660 | 1661 | Binding modes: 1662 | 1663 | 1664 | ``` 1665 | fn main() { 1666 | let x = 5; 1667 | match x { 1668 | ref y => println!("Got a reference to {}", y), 1669 | } 1670 | } 1671 | ``` 1672 | 1673 | 1674 | Binding modes `ref` and `ref mut` allow you to bind to a reference or mutable reference in pattern matching, providing more control over how values are accessed. 1675 | 1676 | Try blocks (experimental): 1677 | 1678 | 1679 | ``` 1680 | #![feature(try_blocks)] 1681 | 1682 | fn main() { 1683 | let result: Result = try { 1684 | let x = "3".parse()?; 1685 | let y = "5".parse()?; 1686 | x + y 1687 | }; 1688 | 1689 | match result { 1690 | Ok(sum) => println!("Sum: {}", sum), 1691 | Err(err) => eprintln!("Error: {}", err), 1692 | } 1693 | } 1694 | ``` 1695 | 1696 | 1697 | Try blocks enable the use of the `?` operator within a block, propagating errors to the enclosing scope (note that this is an experimental feature and requires a nightly Rust build). 1698 | 1699 | Negative implementations: 1700 | 1701 | 1702 | ``` 1703 | struct MyType; 1704 | 1705 | impl !std::fmt::Display for MyType {} 1706 | ``` 1707 | 1708 | 1709 | Negative implementations specify that a given type does not implement a particular trait, providing more precise control over trait bounds and coherence. 1710 | 1711 | Array concatenation (Note: Rust does not support array concatenation directly, but you can achieve this using the `concat` macro from the `arraytools` crate.): 1712 | 1713 | 1714 | ``` 1715 | use arraytools::concat; 1716 | 1717 | fn main() { 1718 | let a = [1, 2, 3]; 1719 | let b = [4, 5, 6]; 1720 | let c = concat!(a, b); 1721 | println!("{:?}", c); 1722 | } 1723 | ``` 1724 | 1725 | 1726 | Array concatenation joins two arrays end-to-end, forming a new array. 1727 | 1728 | Array repeating: 1729 | 1730 | 1731 | ``` 1732 | fn main() { 1733 | let arr = [1; 5]; 1734 | println!("{:?}", arr); 1735 | } 1736 | ``` 1737 | 1738 | 1739 | Array repeating creates an array of length N with all elements set to a. 1740 | 1741 | Scoped lint attributes: 1742 | 1743 | 1744 | ``` 1745 | fn main() { 1746 | #[allow(unused_variables)] 1747 | let x = 5; 1748 | } 1749 | ``` 1750 | 1751 | 1752 | Scoped lint attributes allow or disallow specific lints within a limited scope, providing granular control over compiler warnings. 1753 | 1754 | Lazy static: 1755 | 1756 | 1757 | ``` 1758 | use lazy_static::lazy_static; 1759 | use std::collections::HashMap; 1760 | 1761 | lazy_static! { 1762 | static ref CONSTANTS: HashMap<&'static str, i32> = { 1763 | let mut m = HashMap::new(); 1764 | m.insert("ONE", 1); 1765 | m.insert("TWO", 2); 1766 | m 1767 | }; 1768 | } 1769 | 1770 | fn main() { 1771 | println!("One: {}", CONSTANTS.get("ONE").unwrap()); 1772 | } 1773 | ``` 1774 | 1775 | 1776 | Lazy static enables the declaration of lazily initialized static values, provided by the `lazy_static` crate. 1777 | 1778 | Associated consts: 1779 | 1780 | 1781 | ``` 1782 | trait Named { 1783 | const NAME: &'static str; 1784 | } 1785 | 1786 | struct Person; 1787 | 1788 | impl Named for Person { 1789 | const NAME: &'static str = "Person"; 1790 | } 1791 | 1792 | fn main() { 1793 | println!("Name: {}", Person::NAME); 1794 | } 1795 | 1796 | E); } 1797 | ``` 1798 | 1799 | 1800 | Associated consts allow declaring associated constants within a trait or implementation block. 1801 | 1802 | Existential types (Note: existential types have been replaced by `impl Trait` in type aliases): 1803 | 1804 | 1805 | ``` 1806 | trait Foo {} 1807 | 1808 | struct Bar; 1809 | 1810 | impl Foo for Bar {} 1811 | 1812 | type MyFoo = impl Foo; 1813 | 1814 | fn create_foo() -> MyFoo { 1815 | Bar 1816 | } 1817 | 1818 | fn main() { 1819 | let _foo = create_foo(); 1820 | } 1821 | ``` 1822 | 1823 | 1824 | Existential types (replaced by `impl Trait` in type aliases) allow declaring a type alias for an opaque type that implements a given trait. 1825 | 1826 | Struct update syntax: 1827 | 1828 | 1829 | ``` 1830 | struct Point { 1831 | x: i32, 1832 | y: i32, 1833 | } 1834 | 1835 | fn main() { 1836 | let point1 = Point { x: 1, y: 2 }; 1837 | let point2 = Point { x: 3, ..point1 }; 1838 | println!("point2: x = {}, y = {}", point2.x, point2.y); 1839 | } 1840 | ``` 1841 | 1842 | 1843 | Struct update syntax enables creating a new struct with some fields updated from an existing struct. 1844 | 1845 | Or patterns: 1846 | 1847 | 1848 | ``` 1849 | fn main() { 1850 | let value = Some(1); 1851 | 1852 | match value { 1853 | Some(0) | Some(1) => println!("Matched 0 or 1"), 1854 | Some(_) => println!("Matched another value"), 1855 | None => println!("Matched None"), 1856 | } 1857 | } 1858 | ``` 1859 | 1860 | 1861 | Or patterns allow matching on multiple patterns in a single arm of a `match` or `if let` expression. 1862 | 1863 | Const generics (experimental feature): 1864 | 1865 | 1866 | ``` 1867 | #![feature(const_generics)] 1868 | #![allow(incomplete_features)] 1869 | 1870 | struct Array { 1871 | items: [T; N], 1872 | } 1873 | 1874 | impl Array { 1875 | fn new(value: T) -> Self 1876 | where 1877 | T: Copy, 1878 | { 1879 | Array { items: [value; N] } 1880 | } 1881 | } 1882 | 1883 | fn main() { 1884 | let arr: Array = Array::new(42); 1885 | println!("{:?}", arr.items); 1886 | } 1887 | ``` 1888 | 1889 | 1890 | Const generics enable defining generic parameters with constant values, allowing more powerful compile-time computations. 1891 | 1892 | Inline attributes: 1893 | 1894 | 1895 | ``` 1896 | #[inline(always)] 1897 | fn add(a: i32, b: i32) -> i32 { 1898 | a + b 1899 | } 1900 | 1901 | fn main() { 1902 | let result = add(2, 3); 1903 | println!("Result: {}", result); 1904 | } 1905 | ``` 1906 | 1907 | 1908 | Inline attributes suggest that a function be inlined by the compiler, potentially improving performance at the cost of increased code size. 1909 | 1910 | Trait objects: 1911 | 1912 | 1913 | ``` 1914 | trait Speak { 1915 | fn speak(&self); 1916 | } 1917 | 1918 | struct Dog; 1919 | struct Cat; 1920 | 1921 | impl Speak for Dog { 1922 | fn speak(&self) { 1923 | println!("Woof!"); 1924 | } 1925 | } 1926 | 1927 | impl Speak for Cat { 1928 | fn speak(&self) { 1929 | println!("Meow!"); 1930 | } 1931 | } 1932 | 1933 | fn main() { 1934 | let animals: Vec> = vec![Box::new(Dog), Box::new(Cat)]; 1935 | for animal in animals { 1936 | animal.speak(); 1937 | } 1938 | } 1939 | ``` 1940 | 1941 | 1942 | Trait objects allow creating a dynamically dispatched, heap-allocated object implementing a specific trait. 1943 | 1944 | Automatic dereferencing: 1945 | 1946 | 1947 | ``` 1948 | struct Person { 1949 | name: String, 1950 | } 1951 | 1952 | impl Person { 1953 | fn greet(&self) { 1954 | println!("Hello, {}!", self.name); 1955 | } 1956 | } 1957 | 1958 | fn main() { 1959 | let person = Person { 1960 | name: "Alice".to_string(), 1961 | }; 1962 | let person_ref = &person; 1963 | person_ref.greet(); // Rust automatically dereferences person_ref 1964 | } 1965 | ``` 1966 | 1967 | 1968 | Automatic dereferencing allows calling methods on references or smart pointers without the need to explicitly dereference them using `*`. 1969 | 1970 | Range patterns: 1971 | 1972 | 1973 | ``` 1974 | fn main() { 1975 | let value = 2; 1976 | 1977 | match value { 1978 | 0..=2 => println!("Matched 0, 1, or 2"), 1979 | _ => println!("Matched another value"), 1980 | } 1981 | } 1982 | ``` 1983 | 1984 | 1985 | Range patterns allow matching a range of values inclusively within a match expression. 1986 | 1987 | Nested import groups: 1988 | 1989 | 1990 | ``` 1991 | mod foo { 1992 | pub mod bar { 1993 | pub fn hello() { 1994 | println!("Hello from bar!"); 1995 | } 1996 | } 1997 | 1998 | pub mod baz { 1999 | pub mod qux { 2000 | pub fn hello() { 2001 | println!("Hello from qux!"); 2002 | } 2003 | } 2004 | 2005 | pub mod quux { 2006 | pub fn hello() { 2007 | println!("Hello from quux!"); 2008 | } 2009 | } 2010 | } 2011 | } 2012 | 2013 | use foo::{bar, baz::{qux, quux}}; 2014 | 2015 | fn main() { 2016 | bar::hello(); 2017 | qux::hello(); 2018 | quux::hello(); 2019 | } 2020 | ``` 2021 | 2022 | 2023 | Nested import groups allow importing multiple items from the same module in a more concise way. 2024 | 2025 | Enum variant aliases (Note: Rust does not support enum variant aliases, but you can create a wrapper function to achieve a similar effect.): 2026 | 2027 | 2028 | ``` 2029 | enum Color { 2030 | Red, 2031 | Green, 2032 | Blue, 2033 | } 2034 | 2035 | fn red() -> Color { 2036 | Color::Red 2037 | } 2038 | 2039 | fn main() { 2040 | let color = red(); 2041 | match color { 2042 | Color::Red => println!("It's red!"), 2043 | 2044 | 2045 | 2046 | Color::Green => println!("It's green!"), 2047 | Color::Blue => println!("It's blue!"), 2048 | } 2049 | 2050 | ) 2051 | ``` 2052 | 2053 | 2054 | Destructuring assignment allows unpacking a tuple or struct into individual variables. 2055 | 2056 | 2057 | ``` 2058 | fn main() { 2059 | let tuple = (1, 2); 2060 | let (a, b) = tuple; 2061 | println!("a: {}, b: {}", a, b); 2062 | 2063 | struct Point { 2064 | x: i32, 2065 | y: i32, 2066 | } 2067 | 2068 | let point = Point { x: 3, y: 4 }; 2069 | let Point { x, y } = point; 2070 | println!("x: {}, y: {}", x, y); 2071 | } 2072 | 2073 | "turbofish" for const generics: 2074 | rstruct Array([i32; N]); 2075 | 2076 | fn make_array() -> Array { 2077 | Array([0; N]) 2078 | } 2079 | 2080 | fn main() { 2081 | let array: Array<{ 5 }> = make_array::<{ 5 }>(); 2082 | println!("array length: {}", array.0.len()); 2083 | } 2084 | ``` 2085 | 2086 | 2087 | Use the "turbofish" syntax for const generics to specify the value of a const generic parameter, like in this example with an array of a specified length. 2088 | 2089 | Nested functions: 2090 | 2091 | 2092 | ``` 2093 | fn main() { 2094 | fn greet(name: &str) { 2095 | println!("Hello, {}!", name); 2096 | } 2097 | 2098 | greet("Alice"); 2099 | greet("Bob"); 2100 | } 2101 | ``` 2102 | 2103 | 2104 | Nested functions can be defined inside other functions to help organize code and encapsulate logic. 2105 | 2106 | Visibility shortcut: 2107 | 2108 | 2109 | ``` 2110 | mod my_module { 2111 | pub(crate) fn public_within_crate() { 2112 | println!("This function is visible within the crate"); 2113 | } 2114 | } 2115 | 2116 | fn main() { 2117 | my_module::public_within_crate(); 2118 | } 2119 | ``` 2120 | 2121 | 2122 | Use `pub(crate)` to make a function or item visible only within the current crate. 2123 | 2124 | Inline assembly (requires nightly Rust): 2125 | 2126 | 2127 | ``` 2128 | #![feature(asm)] 2129 | 2130 | fn main() { 2131 | let x: u32; 2132 | unsafe { 2133 | asm!("mov {}, 42", out(reg) x); 2134 | } 2135 | println!("x: {}", x); 2136 | } 2137 | ``` 2138 | 2139 | 2140 | Inline assembly can be used for highly optimized or low-level operations, but it requires nightly Rust and is unsafe. 2141 | 2142 | "or patterns": 2143 | 2144 | 2145 | ``` 2146 | enum Fruit { 2147 | Apple, 2148 | Orange, 2149 | Banana, 2150 | } 2151 | 2152 | fn is_citrus(fruit: Fruit) -> bool { 2153 | match fruit { 2154 | Fruit::Orange | Fruit::Lemon => true, 2155 | _ => false, 2156 | } 2157 | } 2158 | 2159 | fn main() { 2160 | let orange = Fruit::Orange; 2161 | println!("Is orange a citrus fruit? {}", is_citrus(orange)); 2162 | } 2163 | ``` 2164 | 2165 | 2166 | Or patterns in match expressions allow matching multiple patterns in a single arm. 2167 | 2168 | Non-exhaustive enums: 2169 | 2170 | 2171 | ``` 2172 | #[non_exhaustive] 2173 | pub enum Status { 2174 | Ok, 2175 | Error, 2176 | } 2177 | 2178 | fn process_status(status: Status) { 2179 | match status { 2180 | Status::Ok => println!("Everything is OK"), 2181 | Status::Error => println!("There was an error"), 2182 | _ => println!("Unknown status"), 2183 | } 2184 | } 2185 | ``` 2186 | 2187 | 2188 | Non-exhaustive enums signal that an enum may have additional variants in the future, so users should include a catch-all `_` match arm. 2189 | 2190 | Pinning: 2191 | 2192 | 2193 | ``` 2194 | use std::pin::Pin; 2195 | 2196 | fn main() { 2197 | let value = Box::new(42); 2198 | let pinned_value: Pin> = Box::pin(*value); 2199 | 2200 | println!("The pinned value is: {}", *pinned_value); 2201 | } 2202 | ``` 2203 | 2204 | 2205 | The Pin type ensures that the memory address of the value it wraps won't change, which can be useful in certain contexts like async programming or working with self-referential types. 2206 | 2207 | Async closure (requires nightly Rust): 2208 | 2209 | 2210 | ``` 2211 | #![feature(async_closure)] 2212 | 2213 | async fn run_async_closure(f: F) 2214 | where 2215 | F: FnOnce() -> Fut, 2216 | Fut: std::future::Future, 2217 | { 2218 | let result = f().await; 2219 | println!("Result: {}", result); 2220 | } 2221 | 2222 | fn main() { 2223 | let async_closure = async || async { 42 }; 2224 | let fut = run_async_closure(async_closure); 2225 | futures::executor::block_on(fut); 2226 | } 2227 | ``` 2228 | 2229 | 2230 | Async closures allow you to create closures that return a `Future`, but they are currently unstable and require nightly Rust. 2231 | 2232 | Type-level integers (using typenum crate): 2233 | 2234 | 2235 | ``` 2236 | use typenum::{U3, U4}; 2237 | 2238 | fn main() { 2239 | let array1: [i32; U3::to_usize()] = [1, 2, 3]; 2240 | let array2: [i32; U4::to_usize()] = [1, 2, 3, 4]; 2241 | } 2242 | ``` 2243 | 2244 | 2245 | Use type-level integers from the typenum crate to create types with integer parameters, enabling type-level programming techniques. 2246 | 2247 | Wrapping arithmetic: 2248 | 2249 | 2250 | ``` 2251 | use std::num::Wrapping; 2252 | 2253 | fn main() { 2254 | let a = Wrapping(u32::MAX); 2255 | let b = Wrapping(1u32); 2256 | let result = a + b; 2257 | println!("Wrapped addition result: {}", result.0); 2258 | } 2259 | 2260 | } 2261 | ``` 2262 | 2263 | 2264 | The `Wrapping` type performs arithmetic operations that "wrap" on overflow or underflow, useful in low-level or cryptographic applications. 2265 | 2266 | Uninhabited types: 2267 | 2268 | 2269 | ``` 2270 | fn never_returns() -> ! { 2271 | loop { 2272 | // This function never returns 2273 | } 2274 | } 2275 | 2276 | eturns } } 2277 | ``` 2278 | 2279 | 2280 | The `!` type is used to indicate that a function never returns, such as a function that enters an infinite loop. 2281 | 2282 | Multidispatch (limited example): 2283 | 2284 | 2285 | ``` 2286 | trait Animal { 2287 | fn speak(&self); 2288 | } 2289 | 2290 | struct Cat; 2291 | struct Dog; 2292 | 2293 | impl Animal for Cat { 2294 | fn speak(&self) { 2295 | println!("Meow!"); 2296 | } 2297 | } 2298 | 2299 | impl Animal for Dog { 2300 | fn speak(&self) { 2301 | println!("Woof!"); 2302 | } 2303 | } 2304 | 2305 | fn main() { 2306 | let animals: Vec> = vec![Box::new(Cat), Box::new(Dog)]; 2307 | for animal in animals { 2308 | animal.speak(); // Multidispatch based on the actual type of the object 2309 | } 2310 | } 2311 | ``` 2312 | 2313 | 2314 | Rust supports limited multiple dispatch through traits and trait objects, allowing dynamic method resolution at runtime based on object types. 2315 | 2316 | 2317 | ``` 2318 | Custom dereference coercions: 2319 | use std::ops::Deref; 2320 | 2321 | struct Wrapper(String); 2322 | 2323 | impl Deref for Wrapper { 2324 | type Target = str; 2325 | 2326 | fn deref(&self) -> &Self::Target { 2327 | &self.0 2328 | } 2329 | } 2330 | 2331 | fn main() { 2332 | let wrapper = Wrapper(String::from("Hello, world!")); 2333 | let string_ref: &str = &wrapper; 2334 | println!("{}", string_ref); 2335 | } 2336 | 2337 | ; } 2338 | ``` 2339 | 2340 | 2341 | Implementing the `Deref` trait enables custom dereference coercions for your types, overloading the `*` operator. 2342 | 2343 | Safe transmutes (using safe-transmute crate): 2344 | 2345 | 2346 | ``` 2347 | use safe_transmute::guarded_transmute; 2348 | 2349 | fn main() { 2350 | let bytes: &[u8] = &[0, 0, 0, 42]; 2351 | let result = guarded_transmute::(bytes); 2352 | match result { 2353 | Ok(value) => println!("Transmuted value: {}", value), 2354 | Err(_) => println!("Invalid transmutation"), 2355 | } 2356 | } 2357 | ``` 2358 | 2359 | 2360 | The safe-transmute crate 2361 | 2362 | Const generics (available since Rust 1.51): 2363 | 2364 | 2365 | ``` 2366 | struct ArrayWrapper { 2367 | array: [i32; N], 2368 | } 2369 | 2370 | impl ArrayWrapper { 2371 | fn new(array: [i32; N]) -> Self { 2372 | ArrayWrapper { array } 2373 | } 2374 | 2375 | fn sum(&self) -> i32 { 2376 | self.array.iter().sum() 2377 | } 2378 | } 2379 | 2380 | fn main() { 2381 | let wrapper1 = ArrayWrapper::new([1, 2, 3]); 2382 | let wrapper2 = ArrayWrapper::new([1, 2, 3, 4, 5]); 2383 | 2384 | println!("Sum of wrapper1: {}", wrapper1.sum()); 2385 | println!("Sum of wrapper2: {}", wrapper2.sum()); 2386 | } 2387 | 2388 | Const generics allow you to parameterize types over constant values, enabling more expressive and powerful generic programming techniques. 2389 | 2390 | Associated consts: 2391 | trait HasArea { 2392 | const PI: f64; 2393 | 2394 | fn area(&self) -> f64; 2395 | } 2396 | 2397 | struct Circle { 2398 | radius: f64, 2399 | } 2400 | 2401 | impl HasArea for Circle { 2402 | const PI: f64 = 3.14159265359; 2403 | 2404 | fn area(&self) -> f64 { 2405 | Self::PI * self.radius * self.radius 2406 | } 2407 | } 2408 | ``` 2409 | 2410 | 2411 | Associated consts allow you to define constant values within a trait implementation for a specific type. 2412 | 2413 | Unsafe traits: 2414 | 2415 | 2416 | ``` 2417 | unsafe trait UnsafeCounter { 2418 | fn increment(&mut self); 2419 | } 2420 | 2421 | struct Counter { 2422 | value: i32, 2423 | } 2424 | 2425 | unsafe impl UnsafeCounter for Counter { 2426 | fn increment(&mut self) { 2427 | self.value += 1; 2428 | } 2429 | } 2430 | ``` 2431 | 2432 | 2433 | Unsafe traits indicate that implementing the trait requires adhering to certain invariants that the Rust compiler cannot enforce. 2434 | 2435 | Diverging functions: 2436 | 2437 | 2438 | ``` 2439 | fn infinite_loop() -> ! { 2440 | loop {} 2441 | } 2442 | ``` 2443 | 2444 | 2445 | Diverging functions have a special return type !, which is called the "never" type, indicating the function never returns. 2446 | 2447 | Function pointers: 2448 | 2449 | 2450 | ``` 2451 | fn add(x: i32, y: i32) -> i32 { 2452 | x + y 2453 | } 2454 | 2455 | fn main() { 2456 | let add_fn: fn(i32, i32) -> i32 = add; 2457 | println!("Result: {}", add_fn(2, 3)); 2458 | } 2459 | ``` 2460 | 2461 | 2462 | Function pointers store references to functions, allowing you to use them as arguments or return values. 2463 | 2464 | Custom allocators: 2465 | 2466 | 2467 | ``` 2468 | use std::alloc::{GlobalAlloc, Layout, System}; 2469 | 2470 | struct MyAllocator; 2471 | 2472 | unsafe impl GlobalAlloc for MyAllocator { 2473 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 2474 | System.alloc(layout) 2475 | } 2476 | 2477 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 2478 | System.dealloc(ptr, layout) 2479 | } 2480 | } 2481 | 2482 | #[global_allocator] 2483 | static GLOBAL: MyAllocator = MyAllocator; 2484 | ``` 2485 | 2486 | 2487 | Custom allocators let you implement the GlobalAlloc trait and use the #[global_allocator] attribute to specify a custom allocator. 2488 | 2489 | Macros: 2490 | 2491 | 2492 | ``` 2493 | macro_rules! vec { 2494 | ($($x:expr),*) => { 2495 | { 2496 | let mut temp_vec = Vec::new(); 2497 | $( 2498 | temp_vec.push($x); 2499 | )* 2500 | temp_vec 2501 | } 2502 | }; 2503 | } 2504 | 2505 | fn main() { 2506 | let v = vec![1, 2, 3]; 2507 | println!("{:?}", v); 2508 | } 2509 | ``` 2510 | 2511 | 2512 | Macros in Rust use the macro_rules! keyword to define patterns and expansions, allowing you to create reusable chunks of code with a concise syntax. 2513 | 2514 | Custom derive: 2515 | 2516 | 2517 | ``` 2518 | // Create a simple custom derive macro for a trait named "Hello" 2519 | use proc_macro::TokenStream; 2520 | use quote::quote; 2521 | use syn::{parse_macro_input, DeriveInput}; 2522 | 2523 | #[proc_macro_derive(Hello)] 2524 | pub fn hello_derive(input: TokenStream) -> TokenStream { 2525 | let input = parse_macro_input!(input as DeriveInput); 2526 | let name = input.ident; 2527 | 2528 | let expanded = quote! { 2529 | impl Hello for #name { 2530 | fn hello(&self) { 2531 | println!("Hello from {}", stringify!(#name)); 2532 | } 2533 | } 2534 | }; 2535 | 2536 | TokenStream::from(expanded) 2537 | } 2538 | 2539 | // Use the custom derive macro in your code 2540 | #[derive(Hello)] 2541 | struct MyStruct; 2542 | 2543 | fn main() { 2544 | let my_struct = MyStruct; 2545 | my_struct.hello(); 2546 | } 2547 | ``` 2548 | 2549 | 2550 | Custom derive macros can automatically generate trait implementations for your types based on the structure and fields of the type. 2551 | 2552 | Attributes: 2553 | 2554 | 2555 | ``` 2556 | // Conditional compilation 2557 | #[cfg(target_os = "linux")] 2558 | fn on_linux() { 2559 | println!("You are running on Linux!"); 2560 | } 2561 | 2562 | #[cfg(not(target_os = "linux"))] 2563 | fn on_linux() { 2564 | println!("You are not running on Linux."); 2565 | } 2566 | 2567 | // Customizing derives 2568 | #[derive(Debug)] 2569 | struct Point { 2570 | x: i32, 2571 | y: i32, 2572 | } 2573 | 2574 | // Specifying test functions 2575 | #[cfg(test)] 2576 | mod tests { 2577 | use super::*; 2578 | 2579 | #[test] 2580 | fn test_point() { 2581 | let p = Point { x: 3, y: 4 }; 2582 | assert_eq!(p.x, 3); 2583 | assert_eq!(p.y, 4); 2584 | } 2585 | } 2586 | 2587 | fn main() { 2588 | on_linux(); 2589 | let point = Point { x: 1, y: 2 }; 2590 | println!("{:?}", point); 2591 | } 2592 | 2593 | } 2594 | ``` 2595 | 2596 | 2597 | Rust attributes are metadata applied to modules, items, or expressions for various purposes, such as conditional compilation, customizing derives, or specifying test functions. 2598 | -------------------------------------------------------------------------------- /TurbofishAndAssociatedTypes.md: -------------------------------------------------------------------------------- 1 | 2 | ## Understanding the Rust Turbofish operator (::<>) and Associated Types 3 | 4 | By Dragos Ruiu (2023 May 1) 5 | 6 | The turbofish operator is used for specifying type parameters explicitly when type inference is insufficient. It can be confusing due to its unusual syntax. 7 | 8 | The turbofish should be placed after the function or struct name, followed by the type parameters in angle brackets. For example: 9 | 10 | 11 | ``` 12 | let x: Vec<_> = vec![1, 2, 3]; 13 | let y = x.into_iter().collect::>(); 14 | ``` 15 | 16 | 17 | 18 | ### Turbofish Examples 19 | 20 | 21 | ``` 22 | fn main() { 23 | let numbers: Vec = vec![1, 2, 3, 4, 5]; 24 | let sum: i32 = numbers.iter().sum(); 25 | let mean: f64 = sum as f64 / numbers.len() as f64; 26 | let rounded_mean: i32 = mean.round() as i32; 27 | 28 | // Using the turbofish operator to explicitly provide the type parameter 29 | let result: Result = "42".parse::(); 30 | println!("Result: {:?}", result); 31 | } 32 | ``` 33 | 34 | 35 | Rust Turbofish Operator (`::`) 36 | 37 | The Rust turbofish operator is used for type hinting when the type of a value cannot be inferred by the compiler, such as when working with generic functions. The turbofish operator helps Rust developers avoid ambiguity and explicitly specify the type of a generic value. 38 | 39 | Example: 40 | 41 | 42 | ``` 43 | fn main() { 44 | let numbers: Vec = vec![1, 2, 3, 4, 5]; 45 | let sum: u32 = numbers.into_iter().sum(); 46 | let mean = sum as f32 / numbers.len() as f32; 47 | 48 | // The turbofish operator is used to specify the type for the `parse` function 49 | let input = "42"; 50 | let parsed_number = input.parse::().unwrap(); 51 | println!("Parsed number: {}", parsed_number); 52 | } 53 | ``` 54 | 55 | 56 | In this example, the turbofish operator is used with the `parse` function to specify that the input string should be parsed into a `u32` integer. 57 | 58 | ### Turbofish isn’t like a C Cast 59 | 60 | In C, casting is used to convert a value from one type to another explicitly. It is a common operation when dealing with different data types, especially when working with low-level operations or interfacing with external libraries. 61 | 62 | C Cast Example: 63 | 64 | 65 | ``` 66 | #include 67 | 68 | int main() { 69 | int sum = 15; 70 | int count = 5; 71 | 72 | // C cast is used to convert 'sum' and 'count' to float before division 73 | float average = (float)sum / (float)count; 74 | 75 | printf("Average: %f\n", average); 76 | 77 | // C cast is used to convert a float to an int 78 | int rounded_average = (int)(average + 0.5); 79 | printf("Rounded average: %d\n", rounded_average); 80 | 81 | return 0; 82 | } 83 | ``` 84 | 85 | 86 | In this C code, casting is used to convert `sum` and `count` to `float` values before division, and to round the average to the nearest integer. 87 | 88 | The Rust turbofish operator is used for compile time type hinting with generic functions, ensuring that the correct type is used in specific situations. In contrast, C casting is used to generate code to explicitly convert values from one type to another. While they serve different purposes, both the Rust turbofish operator and C casting are essential tools for developers to express their intent and work with various data types in their respective languages. 89 | 90 | 91 | ### Different Types of Rust Types 92 | 93 | In Rust, specific types and associated types serve different purposes. Specific types refer to concrete types in the language, while associated types define a relationship between types within traits. Let's explore these concepts in more detail and provide code examples to illustrate their use and how the compiler infers types. 94 | 95 | 96 | ### Specific Types 97 | 98 | Specific types are concrete types in Rust, such as `i32`, `String`, `Vec`, and custom structs or enums. These types can be used directly in variable declarations, function arguments, and return types. 99 | 100 | For example, consider a function that calculates the square of an integer: 101 | 102 | 103 | ``` 104 | fn square(x: i32) -> i32 { 105 | x * x 106 | } 107 | 108 | fn main() { 109 | let x = 5; 110 | let result = square(x); 111 | println!("The square of {} is {}", x, result); 112 | } 113 | ``` 114 | 115 | 116 | In this example, `i32` is a specific type used as the argument and return type of the `square` function. 117 | 118 | 119 | ### Associated Types 120 | 121 | Associated types are used within traits to define a type placeholder that will be provided by each implementing type. They allow trait methods to work with different types without explicitly specifying them using generics. Associated types are defined using the `type` keyword within a trait definition. 122 | 123 | Here's an example of a trait with an associated type: 124 | 125 | 126 | ``` 127 | trait Drawable { 128 | type Output; 129 | 130 | fn draw(&self) -> Self::Output; 131 | } 132 | 133 | struct Circle { 134 | radius: f32, 135 | } 136 | 137 | struct Square { 138 | side: f32, 139 | } 140 | 141 | impl Drawable for Circle { 142 | type Output = String; 143 | 144 | fn draw(&self) -> Self::Output { 145 | format!("Drawing a circle with radius {}", self.radius) 146 | } 147 | } 148 | 149 | impl Drawable for Square { 150 | type Output = String; 151 | 152 | fn draw(&self) -> Self::Output { 153 | format!("Drawing a square with side {}", self.side) 154 | } 155 | } 156 | 157 | fn main() { 158 | let circle = Circle { radius: 5.0 }; 159 | let square = Square { side: 4.0 }; 160 | 161 | println!("{}", circle.draw()); 162 | println!("{}", square.draw()); 163 | } 164 | ``` 165 | 166 | 167 | In this example, the `Drawable` trait has an associated type `Output`. Each implementing type (`Circle` and `Square`) provides a concrete type for `Output` (`String` in this case). 168 | 169 | Associated types are a powerful feature in Rust that allows you to define a type placeholder within a trait. This type is then implemented by each type that implements the trait. By using associated types, you can create more flexible and reusable code without explicitly specifying generic parameters. Associated types can be seen as a form of "type-level function" where the input is the implementing type, and the output is the associated type. 170 | 171 | 172 | ### Defining Associated Types 173 | 174 | To define an associated type within a trait, use the `type` keyword followed by the name of the associated type. This name will serve as a placeholder for the actual type that will be provided by each implementation of the trait. Here's an example of a trait with an associated type: 175 | 176 | 177 | ``` 178 | trait Summable { 179 | type Output; 180 | 181 | fn sum(&self, other: &Self) -> Self::Output; 182 | } 183 | ``` 184 | 185 | 186 | In this example, the `Summable` trait has an associated type called `Output`. The `sum` function takes a reference to another instance of the same implementing type and returns a value of the associated type `Output`. 187 | 188 | 189 | ### Implementing Associated Types 190 | 191 | When implementing a trait with an associated type for a specific type, you'll need to provide a concrete type for the associated type using the `type` keyword. Here's an example implementation of the `Summable` trait for a custom `Point` struct: 192 | 193 | 194 | ``` 195 | struct Point { 196 | x: f64, 197 | y: f64, 198 | } 199 | 200 | impl Summable for Point { 201 | type Output = Point; 202 | 203 | fn sum(&self, other: &Self) -> Self::Output { 204 | Point { 205 | x: self.x + other.x, 206 | y: self.y + other.y, 207 | } 208 | } 209 | } 210 | ``` 211 | 212 | 213 | In this example, we implement the `Summable` trait for the `Point` struct and specify that the associated type `Output` will be the `Point` type itself. The `sum` function adds the `x` and `y` values of the two points and returns a new `Point` instance. 214 | 215 | 216 | ### Using Associated Types 217 | 218 | Once you have defined and implemented a trait with an associated type, you can use the associated type in functions or other traits that work with the original trait. This allows you to create more flexible and reusable code. Here's an example of a function that takes two `Summable` instances and returns their sum: 219 | 220 | 221 | ``` 222 | fn add(a: &T, b: &T) -> T::Output { 223 | a.sum(b) 224 | } 225 | 226 | fn main() { 227 | let a = Point { x: 1.0, y: 2.0 }; 228 | let b = Point { x: 3.0, y: 4.0 }; 229 | 230 | let result = add(&a, &b); 231 | println!("The sum of the points is ({}, {})", result.x, result.y); 232 | } 233 | ``` 234 | 235 | 236 | In this example, the `add` function takes two references to `Summable` instances, and the return type is specified as `T::Output`, which refers to the associated type `Output` of the `Summable` trait. This allows the `add` function to work with any type that implements the `Summable` trait. 237 | 238 | 239 | ### Another Associated Types Example 240 | 241 | In this example, we'll use associated types to create a flexible storage system for different types of values. 242 | 243 | 244 | #### Defining the Storage Trait 245 | 246 | First, let's define the `Storage` trait with an associated type `Value`. This trait will have methods for setting and getting values. 247 | 248 | 249 | ``` 250 | trait Storage { 251 | type Value; 252 | 253 | fn set(&mut self, value: Self::Value); 254 | fn get(&self) -> &Self::Value; 255 | } 256 | ``` 257 | 258 | 259 | 260 | #### Implementing the Storage Trait 261 | 262 | Now, we'll define a simple `Container` struct and implement the `Storage` trait for it. 263 | 264 | 265 | ``` 266 | struct Container { 267 | value: T, 268 | } 269 | 270 | impl Storage for Container { 271 | type Value = T; 272 | 273 | fn set(&mut self, value: Self::Value) { 274 | self.value = value; 275 | } 276 | 277 | fn get(&self) -> &Self::Value { 278 | &self.value 279 | } 280 | } 281 | ``` 282 | 283 | 284 | In this example, we implement the `Storage` trait for `Container` and specify the associated type `Value` as `T`. The `set` and `get` methods allow us to store and retrieve values of type `T`. 285 | 286 | 287 | #### Using the Storage Trait 288 | 289 | Now, we can create containers for different types of values and use them with the `Storage` trait. 290 | 291 | 292 | ``` 293 | fn main() { 294 | let mut int_container = Container { value: 42 }; 295 | let mut string_container = Container { value: String::from("Hello, world!") }; 296 | 297 | println!("Initial int_container value: {}", int_container.get()); 298 | println!("Initial string_container value: {}", string_container.get()); 299 | 300 | int_container.set(100); 301 | string_container.set(String::from("Rust is great!")); 302 | 303 | println!("Updated int_container value: {}", int_container.get()); 304 | println!("Updated string_container value: {}", string_container.get()); 305 | } 306 | ``` 307 | 308 | 309 | In this example, we create two `Container` instances: one for storing an `i32` value and another for storing a `String`. We can use the `set` and `get` methods to update and retrieve values, demonstrating the flexibility of the `Storage` trait with associated types. 310 | 311 | This example shows how associated types can help create adaptable and reusable code, allowing the `Storage` trait to work with different types of values in a type-safe manner. 312 | 313 | 314 | ### The Iterator trait and associated types 315 | 316 | A popular example of associated types in Rust is the `Iterator` trait from the standard library. The `Iterator` trait is used to define a type placeholder `Item` that will be provided by each implementing type, allowing trait methods to work with different types without explicitly specifying them using generics. 317 | 318 | 319 | #### Defining the Iterator Trait 320 | 321 | Here's a simplified version of the `Iterator` trait, which only contains the associated type `Item` and the `next` method: 322 | 323 | 324 | ``` 325 | pub trait Iterator { 326 | type Item; 327 | 328 | fn next(&mut self) -> Option; 329 | } 330 | ``` 331 | 332 | 333 | The `Iterator` trait has an associated type `Item`, which represents the type of elements the iterator will yield. The `next` method is used to get the next element from the iterator, returning an `Option`. When there are no more elements, `next` returns `None`. 334 | 335 | 336 | #### Implementing the Iterator Trait 337 | 338 | Let's implement the `Iterator` trait for a custom `CountUpTo` struct that iterates through numbers from `start` to `end`: 339 | 340 | 341 | ``` 342 | struct CountUpTo { 343 | start: u32, 344 | end: u32, 345 | } 346 | 347 | impl Iterator for CountUpTo { 348 | type Item = u32; 349 | 350 | fn next(&mut self) -> Option { 351 | if self.start < self.end { 352 | let current = self.start; 353 | self.start += 1; 354 | Some(current) 355 | } else { 356 | None 357 | } 358 | } 359 | } 360 | ``` 361 | 362 | 363 | In this example, we implement the `Iterator` trait for `CountUpTo` and specify the associated type `Item` as `u32`. The `next` method yields numbers from `start` to `end` (exclusive), returning `None` when the iteration is complete. 364 | 365 | 366 | #### Using the Iterator 367 | 368 | Now, let's use the `CountUpTo` iterator to sum numbers in a specified range: 369 | 370 | 371 | ``` 372 | fn main() { 373 | let start = 1; 374 | let end = 5; 375 | 376 | let count_up_to = CountUpTo { start, end }; 377 | let sum: u32 = count_up_to.sum(); 378 | 379 | println!("The sum of numbers from {} to {} is: {}", start, end, sum); 380 | } 381 | ``` 382 | 383 | 384 | In this example, we create a `CountUpTo` iterator and use the `sum` method from the `Iterator` trait to calculate the sum of numbers in the range. The `Iterator` trait allows us to work with different types of iterators without explicitly specifying them using generics, providing a flexible and reusable abstraction. 385 | 386 | 387 | ### Type Inference 388 | 389 | Rust's compiler can often infer the types of variables and expressions without requiring explicit annotations. It does this by analyzing the surrounding code to determine the appropriate type. However, in some cases, type inference may be insufficient, and you'll need to provide explicit type annotations or use the turbofish operator. 390 | 391 | Here's an example of Rust's type inference in action: 392 | 393 | 394 | ``` 395 | trait Drawable { 396 | type Output; 397 | 398 | fn draw(&self) -> Self::Output; 399 | } 400 | 401 | struct Circle { 402 | radius: f32, 403 | } 404 | 405 | struct Square { 406 | side: f32, 407 | } 408 | 409 | impl Drawable for Circle { 410 | type Output = String; 411 | 412 | fn draw(&self) -> Self::Output { 413 | format!("Drawing a circle with radius {}", self.radius) 414 | } 415 | } 416 | 417 | impl Drawable for Square { 418 | type Output = String; 419 | 420 | fn draw(&self) -> Self::Output { 421 | format!("Drawing a square with side {}", self.side) 422 | } 423 | } 424 | 425 | fn main() { 426 | let circle = Circle { radius: 5.0 }; 427 | let square = Square { side: 4.0 }; 428 | 429 | println!("{}", circle.draw()); 430 | println!("{}", square.draw()); 431 | } 432 | ``` 433 | 434 | 435 | 436 | ### Turbofish for Explicit Types 437 | 438 | The compiler infers the types of the variables based on the literals and operations used. However, if type inference is not sufficient, you may need to provide explicit type annotations: 439 | 440 | 441 | ``` 442 | fn main() { 443 | let numbers: Vec<_> = vec![1, 2, 3, 4, 5]; 444 | let even_numbers: Vec = numbers.into_iter().filter(|x| x % 2 == 0).collect(); 445 | } 446 | ``` 447 | 448 | 449 | In this example, we use the turbofish operator `::<>` to specify the type `Vec` for the 450 | 451 | Here's another example of a situation where turbofish is necessary: 452 | 453 | 454 | ``` 455 | fn parse_and_add(a: &str, b: &str) -> Result 456 | where 457 | T: std::ops::Add, 458 | { 459 | let a_parsed = a.parse::()?; 460 | let b_parsed = b.parse::()?; 461 | 462 | Ok(a_parsed + b_parsed) 463 | } 464 | 465 | fn main() { 466 | let a = "3"; 467 | let b = "4"; 468 | 469 | // This line will fail to compile because the type cannot be inferred. 470 | // let sum = parse_and_add(a, b).unwrap(); 471 | 472 | // Using the turbofish operator to disambiguate the type. 473 | let sum: i32 = parse_and_add(a, b).unwrap(); 474 | 475 | // Alternatively, using turbofish directly on the function call. 476 | let sum = parse_and_add::(a, b).unwrap(); 477 | 478 | println!("The sum of {} and {} is: {}", a, b, sum); 479 | } 480 | ``` 481 | 482 | 483 | In this example, we define a generic `parse_and_add` function that takes two strings and attempts to parse them as values of type `T`. If parsing is successful, the function returns the sum of the two values. 484 | 485 | In the `main` function, we try to call the `parse_and_add` function. However, since the function is generic and the types of `a` and `b` are `&str`, the compiler cannot infer the type `T`. As a result, the function call without turbofish will fail to compile. 486 | 487 | To resolve this ambiguity, we can use the turbofish operator in two ways: 488 | 489 | 490 | 491 | 1. Specify the type when binding the result to a variable, like `let sum: i32 = parse_and_add(a, b).unwrap();`. 492 | 2. Use turbofish directly on the function call, like `let sum = parse_and_add::(a, b).unwrap();`. 493 | 494 | Both methods disambiguate the type `T` and allow the code to compile and run successfully. 495 | 496 | 497 | ### Turbofish Example 498 | 499 | Here's an example of a small utility that uses the Turbofish operator to create a generic sum function for different numeric types. 500 | 501 | 502 | ``` 503 | use std::str::FromStr; 504 | 505 | // A generic function to parse a list of strings into a list of numeric values. 506 | fn parse_numeric_list(input: &[&str]) -> Result, ::Err> 507 | where 508 | T: FromStr, 509 | { 510 | input.iter().map(|x| x.parse::()).collect() 511 | } 512 | 513 | // A generic function to compute the sum of a list of numeric values. 514 | fn sum_numeric_list(input: &[T]) -> T 515 | where 516 | T: std::iter::Sum, 517 | { 518 | input.iter().cloned().sum() 519 | } 520 | 521 | fn main() { 522 | let input_i32 = ["1", "2", "3", "4", "5"]; 523 | let input_f64 = ["1.0", "2.0", "3.0", "4.0", "5.0"]; 524 | 525 | let parsed_i32: Result, _> = parse_numeric_list::(&input_i32); 526 | let parsed_f64: Result, _> = parse_numeric_list::(&input_f64); 527 | 528 | match parsed_i32 { 529 | Ok(data) => println!("Sum of i32 values: {}", sum_numeric_list::(&data)), 530 | Err(err) => println!("Error parsing i32 list: {:?}", err), 531 | } 532 | 533 | match parsed_f64 { 534 | Ok(data) => println!("Sum of f64 values: {}", sum_numeric_list::(&data)), 535 | Err(err) => println!("Error parsing f64 list: {:?}", err), 536 | } 537 | } 538 | ``` 539 | 540 | 541 | In this example, we define two generic functions, `parse_numeric_list` and `sum_numeric_list`, which parse a list of strings into a list of numeric values and compute the sum of a list of numeric values, respectively. 542 | 543 | The Turbofish operator is used to explicitly provide the type parameter `T` when calling `parse_numeric_list` and `sum_numeric_list`. This is necessary because the Rust compiler cannot infer the desired numeric type automatically. 544 | 545 | The `main` function demonstrates how to use these generic functions with different numeric types (`i32` and `f64`). It parses two lists of strings into lists of numeric values, computes their sums, and prints the results. The Turbofish operator `::<>` helps ensure the correct types are used when calling the generic functions. 546 | 547 | 548 | ### Turbofish Mistakes 549 | 550 | Some common mistakes new Rust programmers might make when using the turbofish operator are using it too much, or using it with the associated instead of specific type. Also remember to import the appropriate traits when using turbofish. 551 | 552 | Unnecessary use of turbofish: Sometimes, the Rust compiler can infer the correct types without the need for turbofish. New programmers might overuse it when it's not necessary, leading to more verbose code. In many cases, you can let the compiler infer the types, making the code cleaner and more concise. 553 | 554 | When working with traits that have associated types, new Rust programmers might mistakenly use turbofish to specify the associated type. Instead, they should implement the trait for the specific type and use the associated type in the implementation. 555 | 556 | An example example of when not to use turbofish: 557 | 558 | 559 | ``` 560 | trait Example { 561 | type Output; 562 | 563 | fn method(&self) -> Self::Output; 564 | } 565 | 566 | struct Foo; 567 | 568 | impl Example for Foo { 569 | type Output = i32; 570 | 571 | fn method(&self) -> Self::Output { 572 | 42 573 | } 574 | } 575 | 576 | fn main() { 577 | let foo = Foo; 578 | let result = foo.method(); // result is of type i32, no turbofish needed 579 | } 580 | ``` 581 | 582 | 583 | Here's another example where the Rust compiler can successfully infer the type without the need for the turbofish operator: 584 | 585 | 586 | ``` 587 | fn multiply(a: T, b: T) -> T 588 | where 589 | T: std::ops::Mul + Copy, 590 | { 591 | a * b 592 | } 593 | 594 | fn main() { 595 | let a = 5; 596 | let b = 6; 597 | 598 | // Type inference works because a and b are both i32. 599 | let product = multiply(a, b); 600 | 601 | println!("The product of {} and {} is: {}", a, b, product); 602 | } 603 | ``` 604 | 605 | 606 | In this example, we have a generic `multiply` function that takes two values of type `T` and returns their product. We use the `Mul` trait from `std::ops` to ensure that the multiplication operation is supported for type `T`. 607 | 608 | In the `main` function, we define two variables, `a` and `b`, both of type `i32`. When we call the `multiply` function, the Rust compiler can infer the type `T` from the types of `a` and `b`, so there is no need to use the turbofish operator. 609 | 610 | Here's a simple example where the Rust compiler cannot infer the type and the turbofish operator is necessary: 611 | 612 | 613 | ``` 614 | fn main() { 615 | let a = "5"; 616 | let b = "6"; 617 | 618 | // Type inference fails because the parse function is generic and the type cannot be inferred. 619 | // let product = a.parse().unwrap() * b.parse().unwrap(); 620 | 621 | // Using the turbofish operator to disambiguate the type. 622 | let product: i32 = a.parse::().unwrap() * b.parse::().unwrap(); 623 | 624 | println!("The product of {} and {} is: {}", a, b, product); 625 | } 626 | ``` 627 | 628 | 629 | In this example, we have two variables, `a` and `b`, both of type `&str`. We want to parse them as integers and multiply them. The `parse` function is generic, and the Rust compiler cannot infer the type to parse them as because the type of `a` and `b` is `&str`. 630 | 631 | To resolve this ambiguity, we can use the turbofish operator when calling the `parse` function, like this: `a.parse::<i32>().unwrap()`. This explicitly tells the compiler to parse the strings as `i32` values, allowing the code to compile and run successfully. 632 | 633 | Here's another example where the Rust compiler cannot infer the type, and the turbofish operator is necessary: 634 | 635 | 636 | ``` 637 | fn create_vec() -> Vec 638 | where 639 | T: Default + Clone, 640 | { 641 | vec![T::default(); 5] 642 | } 643 | 644 | fn main() { 645 | // Type inference fails because the create_vec function is generic and the type cannot be inferred. 646 | // let my_vec = create_vec(); 647 | 648 | // Using the turbofish operator to disambiguate the type. 649 | let my_vec: Vec = create_vec::(); 650 | 651 | println!("The created vector is: {:?}", my_vec); 652 | } 653 | ``` 654 | 655 | 656 | In this example, we have a generic `create_vec` function that creates a `Vec` of a given type `T`. The function initializes the vector with 5 elements, each having the default value of type `T`. 657 | 658 | In the `main` function, when we call the `create_vec` function, the Rust compiler cannot infer the type `T` because there is no information available to determine the desired type of the vector. 659 | 660 | To resolve this ambiguity, we can use the turbofish operator when calling the `create_vec` function, like this: `create_vec::()`. This explicitly tells the compiler that we want to create a `Vec`, allowing the code to compile and run successfully. 661 | 662 | 663 | ### When The Rust Compiler Can Infer Types 664 | 665 | In Rust, the compiler can infer types in many cases, eliminating the need for the turbofish operator. Type inference works best when there's enough context available to determine the types unambiguously. Some common scenarios where the compiler can infer types are: 666 | 667 | 668 | 669 | 1. Variable assignment: When assigning a value to a variable, the compiler can usually infer the type of the variable based on the value being assigned. 670 | 671 | 672 | ``` 673 | let x = 42; // x is inferred to be i32 674 | let y = "hello"; // y is inferred to be &str 675 | 676 | ``` 677 | 678 | 679 | 680 | 2. Function return values: If a function has an explicit return type, the compiler can infer the type of the expression being returned. 681 | 682 | 683 | ``` 684 | fn add(a: i32, b: i32) -> i32 { 685 | a + b // The type of the expression is inferred to be i32 686 | } 687 | 688 | ``` 689 | 690 | 691 | 692 | 3. Function arguments: When calling a function with non-generic argument types, the compiler can infer the types based on the function's signature. 693 | 694 | 695 | ``` 696 | fn print_length(s: &str) { 697 | println!("Length: {}", s.len()); 698 | } 699 | 700 | let my_string = "hello"; 701 | print_length(my_string); // The type of my_string is inferred to be &str 702 | 703 | ``` 704 | 705 | 706 | 707 | 4. Closures: The compiler can infer the types of closure arguments and return values based on how the closure is used. 708 | 709 | 710 | ``` 711 | let numbers = vec![1, 2, 3, 4, 5]; 712 | let doubled: Vec<_> = numbers.iter().map(|n| n * 2).collect(); 713 | // The type of closure argument n is inferred to be &i32 714 | 715 | ``` 716 | 717 | 718 | 719 | 5. Implementing traits: When implementing a trait for a specific type, the compiler can often infer the types used within the implementation based on the trait definition and the implementing type. 720 | 721 | 722 | ``` 723 | struct MyStruct { 724 | value: String, 725 | } 726 | 727 | impl std::fmt::Display for MyStruct { 728 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 729 | write!(f, "{}", self.value) 730 | // The types of f and self.value are inferred from the trait definition 731 | } 732 | } 733 | ``` 734 | 735 | 736 | In these scenarios, the compiler has enough context to determine the types without ambiguity, so the turbofish operator is not necessary. However, in situations where there isn't enough context, or multiple types could be valid, the turbofish operator is required to disambiguate the type. 737 | 738 | 739 | ### Real-World Turbofish 740 | 741 | In the popular web framework `actix-web`, the Turbofish operator is used to handle HTTP requests with different response types. Let's consider an example from the `actix-web` documentation where we use the Turbofish operator for handling different response types with JSON data. 742 | 743 | 744 | ``` 745 | use actix_web::{web, App, HttpResponse, HttpServer}; 746 | use serde::Deserialize; 747 | 748 | #[derive(Deserialize)] 749 | struct Info { 750 | username: String, 751 | } 752 | 753 | // Handler for the `/info` endpoint that returns a JSON response. 754 | async fn info(info: web::Json) -> HttpResponse { 755 | println!("Model: {:?}", info); 756 | HttpResponse::Ok().json(info.0) // <- send the JSON response 757 | } 758 | 759 | #[actix_web::main] 760 | async fn main() -> std::io::Result<()> { 761 | HttpServer::new(|| { 762 | App::new() 763 | .service(web::resource("/info").route(web::post().to(info))) 764 | }) 765 | .bind("127.0.0.1:8080")? 766 | .run() 767 | .await 768 | } 769 | ``` 770 | 771 | 772 | In this example, we define a simple Actix web server with a single POST endpoint `/info` that accepts and returns JSON data. The `info` handler function accepts a `web::Json` parameter, where `Info` is a struct that derives `Deserialize`. 773 | 774 | When defining the route for the `/info` endpoint, we need to specify how to handle incoming requests, and we use the `to` function from the `actix-web` library. In this case, we want to handle incoming JSON data with the `info` handler function. The Turbofish operator is not explicitly used here, but it's implicitly used by the `actix-web` macro system. 775 | 776 | In some cases, you might want to handle different response types explicitly. For example, if you want to handle both JSON and XML data, you can use the Turbofish operator to specify the desired response type: 777 | 778 | 779 | ``` 780 | use actix_web::{web, App, HttpResponse, HttpServer}; 781 | use serde::Deserialize; 782 | 783 | #[derive(Deserialize)] 784 | struct Info { 785 | username: String, 786 | } 787 | 788 | async fn info_xml(info: web::Json) -> HttpResponse { 789 | println!("Model: {:?}", info); 790 | HttpResponse::Ok().body(format!("{}", info.username)) 791 | } 792 | 793 | #[actix_web::main] 794 | async fn main() -> std::io::Result<()> { 795 | HttpServer::new(|| { 796 | App::new() 797 | .service(web::resource("/info").route(web::post().to::<_, _, _, web::Json>(info))) 798 | .service(web::resource("/info_xml").route(web::post().to::<_, _, _, web::Json>(info_xml))) 799 | }) 800 | .bind("127.0.0.1:8080")? 801 | .run() 802 | .await 803 | } 804 | ``` 805 | 806 | 807 | In this modified example, we added a new endpoint `/info_xml` that accepts JSON data but returns an XML response. We also added the Turbofish operator `::<_, _, _, web::Json>` to the `to` function to explicitly specify the response type. This makes it clear that the handler functions expect a `web::Json` parameter, and the Rust compiler can ensure the correct types are used. 808 | 809 | `<_, _, _, web::Json>` is a type annotation for the `to()` method that specifies the expected type of the handler function. The `_` is a type placeholder, meaning the actual type will be inferred by the Rust compiler. This is useful when you want to provide only part of the type information, and let the compiler fill in the rest. 810 | 811 | In Actix Web, the `to()` method is used to specify the handler function for a route. The handler function takes a number of arguments depending on the configuration of the route. In general, a handler function can take up to 4 arguments: 812 | 813 | 1. `HttpRequest`: Represents the incoming HTTP request. 814 | 2. `Path`: Represents a typed path parameter. 815 | 3. `Query`: Represents a typed query string. 816 | 4. `Payload`: Represents the incoming request payload. 817 | 818 | In the code snippet, the type annotation `<_, _, _, web::Json>` specifies that the handler function expects the fourth argument to be of type `web::Json`. The `web::Json` type is a wrapper for deserialized JSON data. The other three arguments are left as `_`, which means the Rust compiler will infer their types. 819 | 820 | The Turbofish operator is used here to provide more explicit type information for the Actix web framework, ensuring that the handler functions are correctly set up for the specified response types. 821 | 822 | 823 | ### RealWorld Turbofish #2: `serde` 824 | 825 | In this example, we will look at the popular Rust library called `serde`, which is used for serializing and deserializing data structures. The `serde_json` crate is a part of the `serde` ecosystem and provides support for JSON data handling. In certain cases, the Rust compiler cannot infer the type information while using `serde_json`, and the turbofish operator is helpful for specifying types explicitly. 826 | 827 | Consider a simple JSON string that represents a user, and we want to deserialize it into a `User` struct: 828 | 829 | 830 | ``` 831 | use serde::Deserialize; 832 | use serde_json::Value; 833 | 834 | #[derive(Deserialize, Debug)] 835 | struct User { 836 | name: String, 837 | age: u32, 838 | } 839 | 840 | fn main() { 841 | let json = r#" 842 | { 843 | "name": "John Doe", 844 | "age": 30 845 | } 846 | "#; 847 | 848 | let user: User = serde_json::from_str(json).unwrap(); 849 | println!("User: {:?}", user); 850 | } 851 | ``` 852 | 853 | 854 | In the example above, the compiler can infer the type of `user` because it is explicitly annotated with `User`. However, if we want to deserialize JSON data into a more complex data structure, like a `HashMap`, we might need to use the turbofish operator to help the compiler with type inference: 855 | 856 | 857 | ``` 858 | use serde::Deserialize; 859 | use serde_json::Value; 860 | use std::collections::HashMap; 861 | 862 | #[derive(Deserialize, Debug)] 863 | struct User { 864 | name: String, 865 | age: u32, 866 | } 867 | 868 | fn main() { 869 | let json = r#" 870 | { 871 | "user1": { 872 | "name": "John Doe", 873 | "age": 30 874 | }, 875 | "user2": { 876 | "name": "Jane Doe", 877 | "age": 28 878 | } 879 | } 880 | "#; 881 | 882 | let users: HashMap = serde_json::from_str(json).unwrap(); 883 | println!("Users: {:?}", users); 884 | } 885 | ``` 886 | 887 | 888 | In this example, we need to deserialize the JSON data into a `HashMap`. If we were to omit the type annotation for the `users` variable, the compiler would not be able to infer the correct type, and an error would occur. To resolve this issue, we can use the turbofish operator with the `serde_json::from_str` function: 889 | 890 | 891 | ``` 892 | let users = serde_json::from_str::>(json).unwrap(); 893 | ``` 894 | 895 | 896 | By using the turbofish operator, we explicitly specify the type of the data structure we want to deserialize our JSON data into, allowing the compiler to understand our intent and ensuring the correct type is used. 897 | 898 | 899 | 900 | ### Turbofish For Constants ::<{}> 901 | 902 | There is one more special case and notation in Rust (a phrase one should get used to) for the turbofish operator. And that has to do with constants encoded in curly braces. In Rust, you can use constants in turbofish specifications to provide values for const generics. The turbofish syntax is used to explicitly specify the type or value of a generic parameter in a function or struct. 903 | 904 | For example, suppose you have a generic function that takes a const generic parameter `N`: 905 | 906 | 907 | ``` 908 | fn repeat(value: T) -> [T; N] { 909 | [value; N] 910 | } 911 | ``` 912 | 913 | 914 | You can call this function and specify the value of `N` using the `::<{}>` operator like this: 915 | 916 | 917 | ``` 918 | let arr = repeat::(42); 919 | ``` 920 | 921 | 922 | Here, we are calling the `repeat` function with `i32` as the type parameter and `5` as the value of the const generic parameter `N`. 923 | 924 | 925 | 926 | ### What are Const Generics? 927 | 928 | Const generics are a feature in Rust that allow you to define generic types or functions that take a constant value as a parameter. The constant value is specified at compile time and cannot be changed at runtime. This makes const generics useful for defining data structures and algorithms that operate on fixed-size arrays or other types that require a fixed-size parameter. 929 | 930 | To define a generic type or function with a const generic parameter, you use the `const` keyword before the generic parameter name. For example, here's how you could define a generic struct with a const generic parameter that specifies the length of an array: 931 | 932 | 933 | ``` 934 | struct Array { 935 | data: [T; N], 936 | } 937 | ``` 938 | 939 | 940 | In this example, `T` is a generic type parameter, and `N` is a const generic parameter that specifies the length of the array. 941 | 942 | You can then create instances of this struct with different values of `T` and `N`. For example: 943 | 944 | 945 | ``` 946 | let array1: Array = Array { data: [1, 2, 3, 4, 5] }; 947 | let array2: Array<&str, 3> = Array { data: ["hello", "world", "!"] }; 948 | ``` 949 | 950 | 951 | 952 | ### Using Const Generics in Functions 953 | 954 | You can also use const generics in function signatures. For example, here's how you could define a function that takes a const generic parameter `N` and returns an array of that length filled with a given value: 955 | 956 | 957 | ``` 958 | fn create_array(value: T) -> [T; N] { 959 | [value; N] 960 | } 961 | ``` 962 | 963 | 964 | In this example, `T` is a generic type parameter, and `N` is a const generic parameter that specifies the length of the array. The function returns an array of length `N` filled with the value `value`. 965 | 966 | You can then call this function with different values of `T` and `N`. For example: 967 | 968 | 969 | ``` 970 | let array1: [i32; 5] = create_array(0); 971 | let array2: [&str; 3] = create_array("hello"); 972 | ``` 973 | 974 | 975 | Note that the `Copy` trait bound is required for the type parameter `T` because the function needs to copy the value `value` into each element of the array. 976 | 977 | 978 | ### Generic Constant Turbofish 979 | 980 | The `turbofish` operator `::<{ value }>()` is used to explicitly specify the value of a const generic parameter when calling a generic function. The `value` is an expression that evaluates to a constant value, and it must be enclosed in curly braces to indicate that it is a constant expression. 981 | 982 | In Rust, you can use constants in turbofish specifications to provide values for const generics. The turbofish syntax is used to explicitly specify the type or value of a generic parameter in a function or struct. 983 | 984 | Continuing with the example from above, let's say you have a generic function that takes a const generic parameter `N`: 985 | 986 | 987 | ``` 988 | fn repeat(value: T) -> [T; N] { 989 | [value; N] 990 | } 991 | ``` 992 | 993 | 994 | You can call this function and specify the value of `N` using the turbofish syntax like this: 995 | 996 | 997 | ``` 998 | let arr = repeat::(42); 999 | ``` 1000 | 1001 | 1002 | In this example, we are calling the `repeat` function with `i32` as the type parameter and `5` as the value of the const generic parameter `N`. 1003 | 1004 | Now, let's say you want to use a constant to specify the value of `N`. You can define a constant using the `const` keyword and assign it a value, like this: 1005 | 1006 | 1007 | ``` 1008 | const SIZE: usize = 5; 1009 | ``` 1010 | 1011 | 1012 | Then, you can use the constant in the turbofish specification like this: 1013 | 1014 | 1015 | ``` 1016 | let arr = repeat::(42); 1017 | ``` 1018 | 1019 | 1020 | This will produce the same result as the previous example. 1021 | 1022 | Note that when using a constant in a turbofish specification, the constant must have a type that can be inferred by the compiler. For example, in the previous example, the constant `SIZE` has the type `usize`, which matches the type of the const generic parameter `N`. 1023 | 1024 | The `turbofish ::<> ` notation can be especially useful when you have a complex expression that evaluates to a constant value, because it allows you to specify the value directly in the function call instead of assigning it to a separate constant and then using that constant as the parameter. 1025 | 1026 | You can also use more complex expressions in turbofish specifications, as long as they evaluate to a constant value. For example: 1027 | 1028 | 1029 | ``` 1030 | const SIZE: usize = 2 + 3; 1031 | let arr = repeat::(42); 1032 | ``` 1033 | 1034 | 1035 | In this example, the constant `SIZE` is assigned the value `2 + 3`, which evaluates to `5`. The `repeat` function is then called with `i32` as the type parameter and `SIZE` as the value of the const generic parameter `N`. 1036 | 1037 | Note that the `::<>` notation is only necessary when the compiler cannot infer the value of the const generic parameter from the context. If the value of the const generic parameter can be inferred, you can omit the `::<> `notation and simply call the function like this: 1038 | 1039 | 1040 | ``` 1041 | let arr = repeat(42); 1042 | ``` 1043 | 1044 | 1045 | Here, the value of `N` is inferred to be `1`, because we are passing a single value to the function. 1046 | 1047 | ### Real World Const Turbofish 1048 | 1049 | Here's an example using the Rust standard library that uses the curly brace notation inside the `::<{const}>` turbofish operator: 1050 | 1051 | 1052 | ``` 1053 | use std::mem; 1054 | 1055 | fn main() { 1056 | // Create an array with a fixed size of 8 elements 1057 | let mut arr: [i32; 8] = [0; 8]; 1058 | 1059 | // Get a raw pointer to the start of the array 1060 | let ptr: *mut i32 = arr.as_mut_ptr(); 1061 | 1062 | // Use the `write_bytes` method from the `mem` module to set the 1063 | // entire array to a specific value 1064 | unsafe { 1065 | mem::write_bytes::<{mem::size_of::()}>(ptr, 42, arr.len()); 1066 | } 1067 | 1068 | // Print the array to verify that all elements are set to 42 1069 | for i in 0..arr.len() { 1070 | println!("{}", arr[i]); 1071 | } 1072 | } 1073 | ``` 1074 | 1075 | 1076 | In this example, the `write_bytes` method from the `mem` module is used to set all elements of an array to a specific value. The method takes three arguments: a raw pointer to the start of the array, the value to set all elements to, and the number of elements to set. 1077 | 1078 | The `write_bytes` method is generic over the size of the value being written, which is determined using the `size_of` function from the `mem` module. The size is specified using the `::<{const}>` turbofish operator, where `{const}` is replaced with a constant expression that evaluates to the size of the value in bytes. 1079 | 1080 | In the example, the size of the value being written is the size of an `i32`, which is 4 bytes. This value is computed using the `size_of` function and enclosed in curly braces to indicate that it is a constant expression. The `write_bytes` method is then called with this value as the type parameter using the `::<{const}>` turbofish operator. 1081 | 1082 | This is an example of how the curly brace notation can be used inside the `::<{const}>` turbofish operator to specify a constant expression for a generic parameter in a function call. 1083 | 1084 | ### Turbofish Recap 1085 | 1086 | The Rust turbofish operator is an essential tool for working with generic functions and associated types, as it helps explicitly specify type information that the compiler cannot infer. 1087 | 1088 | The syntax for the turbofish operator is `::`, and it is used in conjunction with a function call or type constructor to provide the necessary type hints. The turbofish operator is particularly useful when working with associated types, which are types that are specified within the context of a trait or type definition. Associated types help create more flexible and reusable code by allowing the implementation of a trait to define the specific types associated with that implementation. 1089 | 1090 | When working with associated types in Rust, it is common to encounter situations where the compiler cannot determine the correct type based solely on the surrounding context. In these cases, the turbofish operator comes in handy, enabling developers to provide the missing type information explicitly. 1091 | 1092 | For example, consider a trait with an associated type `Output` and a generic function that returns the `Output` type. When calling this function, the compiler may not know which specific type to use for `Output`. By using the turbofish operator, the developer can explicitly specify the desired type, ensuring that the function operates correctly and adhering to the principle of strong static typing that Rust is built upon. 1093 | 1094 | The turbofish syntax can also be used to specify constant values for const generic parameters in functions or structs. The `::<{ value }>` notation allows you to explicitly provide the value of a const generic parameter, which is enclosed in curly braces to indicate that it is a constant expression. This notation can be used with simple values or with more complex expressions that evaluate to a constant value. Using the turbofish syntax with constants can make it easier to specify const generic parameters and can improve code readability, especially when the constant value is used in multiple places. 1095 | 1096 | The Rust turbofish operator is a powerful tool for working with generic functions and associated types, providing developers with a means to disambiguate type information and create more robust, maintainable code. 1097 | --------------------------------------------------------------------------------