├── .gitignore ├── Cargo.toml ├── README.md ├── LICENSE-MIT ├── src ├── bin │ ├── example-println.rs │ └── example-frozentree.rs └── lib.rs └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stackpin" 3 | version = "0.0.2" 4 | authors = ["Louis Dureuil "] 5 | license = "MIT OR Apache-2.0" 6 | description = "Crate for data that should be pinned to the stack at the point of declaration." 7 | repository = "https://github.com/dureuill/stackpin" 8 | documentation = "https://docs.rs/stackpin" 9 | readme = "README.md" 10 | keywords = ["pin", "ownership", "self", "reference"] 11 | categories = ["rust-patterns"] 12 | edition = "2018" 13 | 14 | [badges] 15 | maintenance = { status = "experimental" } 16 | 17 | [dependencies] 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | stackpin 2 | ======== 3 | 4 | This crate exposes a `StackPinned` type that allows to represent `!Unpin` data that should be pinned to the stack at the point of declaration. 5 | 6 | To do so, this crate provides a `FromUnpinned` trait and a `stack_let!` macro that enable safe construction of `Pin` objects (aliased to `PinStack` for short). 7 | 8 | Getting instances pinned at the point of declaration is as easy as: 9 | 10 | ```rust 11 | stack_let!(unmovable = Unmovable::new_unpinned("Intel the Beagle")); // this creates the unmovable instance on the stack and binds `unmovable` with a `PinStack` 12 | ``` 13 | 14 | For `Unmovable` a struct implementing the `FromUnpinned` trait. 15 | 16 | See the [crate documentation](https://docs.rs/stackpin) for details, or look directly [at the examples](https://github.com/dureuill/stackpin/tree/master/src/bin). 17 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/bin/example-println.rs: -------------------------------------------------------------------------------- 1 | use stackpin::stack_let; 2 | use stackpin::FromUnpinned; 3 | use stackpin::Unpinned; 4 | use std::marker::PhantomPinned; 5 | 6 | struct Unmovable { 7 | s: String, 8 | _pinned: PhantomPinned, 9 | } 10 | 11 | unsafe impl FromUnpinned for Unmovable { 12 | type PinData = (); 13 | 14 | unsafe fn from_unpinned(s: String) -> (Self, ()) { 15 | ( 16 | Self { 17 | s, 18 | _pinned: PhantomPinned, 19 | }, 20 | (), 21 | ) 22 | } 23 | 24 | unsafe fn on_pin(&mut self, _data: ()) { 25 | // do nothing 26 | } 27 | } 28 | 29 | impl Drop for Unmovable { 30 | fn drop(&mut self) { 31 | println!("Dropped {}", self.s) 32 | } 33 | } 34 | 35 | impl Unmovable { 36 | fn new_unpinned>(s: T) -> Unpinned { 37 | Unpinned::new(s.into()) 38 | } 39 | } 40 | 41 | fn main() { 42 | stack_let!(unmovable = Unmovable::new_unpinned("Beagle")); 43 | ::std::mem::forget(unmovable); // Cannot forget the unmovable itself 44 | 45 | { 46 | stack_let!(unmovable = Unmovable::new_unpinned("Totoro")); 47 | { 48 | ::std::mem::forget(unmovable); // Cannot drop earlier 49 | } 50 | println!("Totoro not dropped yet") 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /src/bin/example-frozentree.rs: -------------------------------------------------------------------------------- 1 | //! This example demonstrates a "FrozenTree" where nodes contain safe back pointers to their parents. 2 | //! 3 | //! To build a `FrozenTree`, one starts with a `Tree` and add the various nodes of the tree. 4 | //! When all nodes are added, the `Tree` is pinned to stack by using it as source in a call to the `stack_let` macro. 5 | //! During this operation, it becomes a `FrozenTree` where each node creates a backlink to its parent, at the cost of the resulting 6 | //! becoming "frozen", i.e. nodes can't be added/removed anymore. 7 | //! 8 | //! Note that this is an example implementation provided for demonstration purpose, and is not in any way 9 | //! a "production-grade" implementation. 10 | pub mod frozen_tree { 11 | 12 | use stackpin::FromUnpinned; 13 | use std::pin::Pin; 14 | use std::ptr::NonNull; 15 | 16 | pub struct Tree { 17 | root: Node, 18 | } 19 | 20 | pub struct Node { 21 | frozen: FrozenNode, 22 | } 23 | 24 | pub struct FrozenTree { 25 | root: FrozenNode, 26 | _pinned: std::marker::PhantomPinned, 27 | } 28 | 29 | pub struct FrozenNode { 30 | data: T, 31 | parent: Option>>, 32 | children: Vec>, 33 | } 34 | 35 | impl FrozenNode { 36 | fn on_pin(&mut self) { 37 | // Note: the implementation of on_pin could use a "pointer bump" algorithm 38 | // similar to PointerDepthFirstIterator, but we simply use a recursive version 39 | // for simplicity. 40 | let r = &*self; 41 | let parent_ptr = NonNull::from(r); 42 | for child in self.children.iter_mut() { 43 | child.on_pin(); 44 | child.parent = Some(parent_ptr); 45 | } 46 | } 47 | 48 | /// # Safety 49 | /// 50 | /// This method performs type punning from a `&FrozenNode` to a `&Node` 51 | /// 52 | /// Since Node is `#[repr(transparent)]`, the pointer cast is legal. 53 | /// 54 | /// However, unsafety could arise if this is used to get a Node from a 55 | /// `FrozenTree` and add new children to that node. 56 | /// 57 | /// Since this returns a `&Node`, on which adding children is not possible, this is perfectly safe. 58 | /// 59 | /// This method is meant to be used to extract the `Node` representation of a child node in a `Tree` 60 | /// (not `FrozenTree`). 61 | fn as_node(&self) -> &Node { 62 | // safety: compatible representation, pointer is not wild since it comes from `self` 63 | unsafe { &*(self as *const FrozenNode as *const Node) } 64 | } 65 | 66 | /// # Safety 67 | /// 68 | /// This method performs type punning from a `&mut FrozenNode` to a `&mut Node` 69 | /// 70 | /// Since Node is `#[repr(transparent)]`, the pointer cast is legal. 71 | /// 72 | /// However, unsafety could arise if this is used to get a Node from a 73 | /// `FrozenTree` and add new children to that node. 74 | /// 75 | /// This method is meant to be used to extract the `Node` representation of a child node in a `Tree` 76 | /// (not `FrozenTree`). 77 | unsafe fn as_node_mut(&mut self) -> &mut Node { 78 | &mut *(self as *mut FrozenNode as *mut Node) 79 | } 80 | } 81 | 82 | unsafe impl FromUnpinned> for FrozenTree { 83 | type PinData = (); 84 | 85 | unsafe fn from_unpinned(tree: Tree) -> (Self, ()) { 86 | ( 87 | // Just move the existing root to the `FrozenTree`. 88 | Self { 89 | root: tree.root.frozen, 90 | _pinned: std::marker::PhantomPinned, 91 | }, 92 | (), 93 | ) 94 | } 95 | 96 | unsafe fn on_pin(&mut self, _data: ()) { 97 | // recursively "fix up" the back reference of each node so that it points to its parent node 98 | self.root.on_pin() 99 | } 100 | } 101 | 102 | impl Node { 103 | pub fn add_child(&mut self, data: T) -> &Self { 104 | self.frozen.children.push(FrozenNode::new(data)); 105 | self.frozen.children.last().unwrap().as_node() 106 | } 107 | 108 | pub fn add_child_mut(&mut self, data: T) -> &mut Self { 109 | self.frozen.children.push(FrozenNode::new(data)); 110 | // safety: we are a `Node`, contained in a `Tree` (not `FrozenTree`) 111 | unsafe { self.frozen.children.last_mut().unwrap().as_node_mut() } 112 | } 113 | } 114 | 115 | impl Tree { 116 | pub fn new(root_data: T) -> Self { 117 | Self { 118 | root: Node { 119 | frozen: FrozenNode::new(root_data), 120 | }, 121 | } 122 | } 123 | 124 | pub fn root(&self) -> &Node { 125 | &self.root 126 | } 127 | 128 | pub fn root_mut(&mut self) -> &mut Node { 129 | &mut self.root 130 | } 131 | } 132 | 133 | impl FrozenNode { 134 | fn new(data: T) -> Self { 135 | Self { 136 | data, 137 | children: Vec::new(), 138 | parent: None, 139 | } 140 | } 141 | 142 | pub fn data(&self) -> &T { 143 | &self.data 144 | } 145 | 146 | pub fn data_mut(&mut self) -> &mut T { 147 | &mut self.data 148 | } 149 | 150 | pub fn pin_data_mut(pin: Pin<&mut Self>) -> &mut T { 151 | // safety: one cannot move the node itself even when moving its data. 152 | unsafe { &mut pin.get_unchecked_mut().data } 153 | } 154 | 155 | pub fn iter_children(&self) -> impl DoubleEndedIterator { 156 | self.children.iter() 157 | } 158 | 159 | pub fn iter_children_mut(&mut self) -> impl DoubleEndedIterator { 160 | self.children.iter_mut() 161 | } 162 | 163 | pub fn parent(&self) -> Option<&Self> { 164 | // safety: parent has been set in the on_pin method and cannot be a wild pointer 165 | self.parent.map(|p| unsafe { &*p.as_ptr() }) 166 | } 167 | } 168 | 169 | impl FrozenTree { 170 | pub fn root(&self) -> &FrozenNode { 171 | &self.root 172 | } 173 | 174 | pub fn root_mut(&mut self) -> &mut FrozenNode { 175 | &mut self.root 176 | } 177 | 178 | pub fn iter_depth_first(&self) -> impl Iterator> { 179 | DepthFirstIterator { 180 | visit_stack: std::iter::once(&self.root).collect(), 181 | } 182 | } 183 | 184 | pub fn iter_depth_first_pointer(&self) -> impl Iterator> { 185 | // Should theorically never happen since FrozenNode contains a Vec 186 | assert!(std::mem::size_of::>() > 0); 187 | PointerDepthFirstIterator { 188 | current_node: Some(self.root()), 189 | } 190 | } 191 | 192 | pub fn iter_breadth_first(&self) -> impl Iterator> { 193 | BreadthFirstIterator { 194 | visit_stack: std::iter::once(&self.root).collect(), 195 | } 196 | } 197 | } 198 | 199 | use std::collections::VecDeque; 200 | 201 | struct DepthFirstIterator<'a, T> { 202 | visit_stack: VecDeque<&'a FrozenNode>, 203 | } 204 | 205 | impl<'a, T: 'a> Iterator for DepthFirstIterator<'a, T> { 206 | type Item = &'a FrozenNode; 207 | 208 | fn next(&mut self) -> Option<&'a FrozenNode> { 209 | self.visit_stack.pop_back().map(|node| { 210 | self.visit_stack.extend(node.iter_children().rev()); 211 | node 212 | }) 213 | } 214 | } 215 | 216 | struct BreadthFirstIterator<'a, T> { 217 | visit_stack: VecDeque<&'a FrozenNode>, 218 | } 219 | 220 | impl<'a, T: 'a> Iterator for BreadthFirstIterator<'a, T> { 221 | type Item = &'a FrozenNode; 222 | 223 | fn next(&mut self) -> Option<&'a FrozenNode> { 224 | self.visit_stack.pop_front().map(|node| { 225 | self.visit_stack.extend(node.iter_children()); 226 | node 227 | }) 228 | } 229 | } 230 | 231 | struct PointerDepthFirstIterator<'a, T> { 232 | current_node: Option<&'a FrozenNode>, 233 | } 234 | 235 | impl<'a, T: 'a> Iterator for PointerDepthFirstIterator<'a, T> { 236 | type Item = &'a FrozenNode; 237 | 238 | fn next(&mut self) -> Option<&'a FrozenNode> { 239 | let current_node = self.current_node?; 240 | let next_node = current_node 241 | .children 242 | .first() 243 | .or_else(|| Self::get_next_node(current_node)); 244 | std::mem::replace(&mut self.current_node, next_node) 245 | } 246 | } 247 | 248 | impl<'a, T: 'a> PointerDepthFirstIterator<'a, T> { 249 | fn get_next_node(mut current_node: &'a FrozenNode) -> Option<&'a FrozenNode> { 250 | loop { 251 | let parent = current_node.parent()?; 252 | // compute address in parent 253 | if let Some(node) = get_next_sibling(current_node, parent) { 254 | return Some(node); 255 | } 256 | current_node = parent; 257 | } 258 | } 259 | } 260 | 261 | fn ptr_distance_from(base: *const FrozenNode, offset: *const FrozenNode) -> usize { 262 | let base = base as usize; 263 | let offset = offset as usize; 264 | let distance = offset - base; 265 | distance / std::mem::size_of::>() 266 | } 267 | 268 | fn get_next_sibling<'a, T>( 269 | current: &'a FrozenNode, 270 | parent: &'a FrozenNode, 271 | ) -> Option<&'a FrozenNode> { 272 | let self_pointer = current as *const FrozenNode; 273 | let children_base = parent.children.as_ptr(); 274 | let index = ptr_distance_from(children_base, self_pointer); 275 | parent.children.get(index + 1) 276 | } 277 | 278 | } 279 | 280 | use stackpin::stack_let; 281 | 282 | use frozen_tree::*; 283 | 284 | fn main() { 285 | let mut tree = Tree::new("root"); 286 | let a = tree.root_mut().add_child_mut("A"); 287 | let aa = a.add_child_mut("AA"); 288 | aa.add_child("AAA"); 289 | a.add_child("AB"); 290 | let b = tree.root_mut().add_child_mut("B"); 291 | b.add_child("BA"); 292 | let bb = b.add_child_mut("BB"); 293 | bb.add_child_mut("BBA"); 294 | stack_let!(tree: FrozenTree<_> = tree); 295 | for child in tree.root().iter_children() { 296 | assert!(child.parent().unwrap().data() == tree.root().data()) 297 | } 298 | assert_eq!( 299 | tree.iter_depth_first() 300 | .map(|node| *node.data()) 301 | .collect::>(), 302 | vec!["root", "A", "AA", "AAA", "AB", "B", "BA", "BB", "BBA"] 303 | ); 304 | 305 | assert_eq!( 306 | tree.iter_depth_first_pointer() 307 | .map(|node| *node.data()) 308 | .collect::>(), 309 | vec!["root", "A", "AA", "AAA", "AB", "B", "BA", "BB", "BBA"] 310 | ); 311 | 312 | assert_eq!( 313 | tree.iter_breadth_first() 314 | .map(|node| *node.data()) 315 | .collect::>(), 316 | vec!["root", "A", "B", "AA", "AB", "BA", "BB", "AAA", "BBA"] 317 | ); 318 | } 319 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The `stackpin` crate exposes a [`StackPinned`] type that allows to represent [`!Unpin`] data that should be [pinned](https://doc.rust-lang.org/std/pin/index.html) to the stack 2 | //! at the point of declaration. 3 | //! The crate exposes a trait, [`FromUnpinned`], as well as a [`stack_let`] macro that makes safely creating [`StackPinned`] instances easier. 4 | //! The crate also exposes the [`PinStack`] type alias for `Pin>`. 5 | //! 6 | //! This crate was inspired from the [pin-utils] crate, the main differences being: 7 | //! * [pin-utils] provides a macro to return a `Pin<&mut T>` instance, 8 | //! with a "mutable reference" semantics that includes reborrow. The `stackpin` crate promotes a 9 | //! "root handle" semantics that guarantees that a function consuming a [`PinStack`] consumes 10 | //! the *only* handle to `T`, and not a reborrowed reference. 11 | //! * The syntax for the `stack_let!(mut id : ty = expr)` macro attempts to mimic a regular `let mut id : ty = expr` statement. 12 | //! * The provided [`FromUnpinned`] trait and [`Unpinned`] struct aim at separating unmovable types 13 | //! from the data that can be used to construct them. `stackpin` aims at promoting a model where 14 | //! all unmovable types are only accessible once pinned. 15 | //! * The [`StackPinned`] type expresses strong guarantee about the fact that the destructor for 16 | //! `T` will be run. 17 | //! * The `stackpin` crate solely focuses on stack pinning. The [pin-utils] crate also provides 18 | //! other utilities such as pin projection. 19 | //! 20 | //! # Stack pinnable types 21 | //! 22 | //! A type T that wants to benefit from the guarantees provided by [`StackPinned`] should be 23 | //! [`!Unpin`]. This is necessary to enforce the "drop will be run" guarantee. 24 | //! 25 | //! Additionally, the `stackpin` crate promotes an idiom where "unmovable" types are strictly 26 | //! separated from movable types, and are preferably only accessible through `PinStack`. 27 | //! 28 | //! For example, let's consider the following `Unmovable` struct (from the [documentation for the 29 | //! `pin` module](https://doc.rust-lang.org/std/pin/index.html)): 30 | //! ``` 31 | //! use std::marker::PhantomPinned; 32 | //! use std::ptr::NonNull; 33 | //! struct Unmovable { 34 | //! // Owned data 35 | //! s: String, 36 | //! // Self referential pointer meant to point to `s` 37 | //! slice: NonNull, 38 | //! // Obligatory marker that makes this struct `!Unpin`. 39 | //! // Without this, implementing `FromUnpinned` for `Unmovable` would not be safe. 40 | //! _pinned: PhantomPinned, 41 | //! } 42 | //! ``` 43 | //! 44 | //! It is important to note that this struct is **not** unmovable by itself, as there are no such types in Rust. 45 | //! Instead, we are going to enforce this through privacy: since the fields of the struct are private, no instance can be created 46 | //! from outside the module. 47 | //! Similarly, no public "constructor" function `pub fn new() -> Unmovable` should be provided. 48 | //! 49 | //! So, how will clients consume `Unmovable` instances? 50 | //! 51 | //! The recommended solution using `stackpin` is to implement `FromUnpinned` for `Unmovable`, where `Data` is the 52 | //! type that would normally serve as parameters in a "constructor" function. 53 | //! ``` 54 | //! # use std::marker::PhantomPinned; 55 | //! # use std::ptr::NonNull; 56 | //! # struct Unmovable { 57 | //! # s: String, 58 | //! # slice: NonNull, 59 | //! # _pinned: PhantomPinned, 60 | //! # } 61 | //! use stackpin::FromUnpinned; 62 | //! // An `Unmovable` can be created from a `String` 63 | //! unsafe impl FromUnpinned for Unmovable { 64 | //! // This associated type can be used to retain information between the creation of the instance and its pinning. 65 | //! // This allows for some sort of "two-steps initialization" without having to store the initialization part in the 66 | //! // type itself. 67 | //! // Here, we don't need it, so we just set it to `()`. 68 | //! type PinData = (); 69 | //! 70 | //! // Simply builds the Unmovable from the String. 71 | //! // The implementation of this function is not allowed to consider that the type won't ever move **yet**. 72 | //! // (in particular, the `Self` instance is returned by this function) 73 | //! // Note, however, that safe users of FromUnpinned will: 74 | //! // * Not do anything to with the returned `Self` instance between the call to 75 | //! // `from_unpinned` and the call to `on_pin`. 76 | //! // * Not panic between calling the two functions 77 | //! // * Always call the second function if the first has been called. 78 | //! unsafe fn from_unpinned(s: String) -> (Self, ()) { 79 | //! ( 80 | //! Self { 81 | //! s, 82 | //! // We will "fix" this dangling pointer once the data will be pinned 83 | //! // and guaranteed not to move anymore. 84 | //! slice: NonNull::dangling(), 85 | //! _pinned: PhantomPinned, 86 | //! }, 87 | //! (), 88 | //! ) 89 | //! } 90 | //! 91 | //! // Performs a second initialization step on an instance that is already guaranteed to never move again. 92 | //! // This allows to e.g. set self borrow with the guarantee that they will remain valid. 93 | //! unsafe fn on_pin(&mut self, _data: ()) { 94 | //! // Data will never move again, set the pointer to our own internal String whose address 95 | //! // will never change anymore 96 | //! self.slice = NonNull::from(&self.s); 97 | //! } 98 | //! } 99 | //! ``` 100 | //! With `FromUnpinned` implemented for `T`, one can now add a "constructor method" that would return an 101 | //! `Unpinned`. The `Unpinned` struct is a simple helper struct around `U` that maintains the destination 102 | //! type `T`. This is used by the [`stack_let`] macro to infer the type of `T` that the user may want to produce. 103 | //! 104 | //! ``` 105 | //! # use std::marker::PhantomPinned; 106 | //! # use std::ptr::NonNull; 107 | //! # struct Unmovable { 108 | //! # s: String, 109 | //! # slice: NonNull, 110 | //! # _pinned: PhantomPinned, 111 | //! # } 112 | //! # use stackpin::Unpinned; 113 | //! # use stackpin::FromUnpinned; 114 | //! # unsafe impl FromUnpinned for Unmovable { 115 | //! # type PinData = (); 116 | //! # unsafe fn from_unpinned(s: String) -> (Self, ()) { 117 | //! # ( 118 | //! # Self { 119 | //! # s, 120 | //! # slice: NonNull::dangling(), 121 | //! # _pinned: PhantomPinned, 122 | //! # }, 123 | //! # (), 124 | //! # ) 125 | //! # } 126 | //! # unsafe fn on_pin(&mut self, _data: ()) { 127 | //! # self.slice = NonNull::from(&self.s); 128 | //! # } 129 | //! # } 130 | //! impl Unmovable { 131 | //! fn new_unpinned>(s: T) -> Unpinned { 132 | //! Unpinned::new(s.into()) 133 | //! } 134 | //! } 135 | //! ``` 136 | //! 137 | //! Then, a user of the `Unmovable` struct can simply build an instance by using the [`stack_let`] macro: 138 | //! ``` 139 | //! # use std::marker::PhantomPinned; 140 | //! # use std::ptr::NonNull; 141 | //! # struct Unmovable { 142 | //! # s: String, 143 | //! # slice: NonNull, 144 | //! # _pinned: PhantomPinned, 145 | //! # } 146 | //! # use stackpin::Unpinned; 147 | //! # use stackpin::FromUnpinned; 148 | //! # unsafe impl FromUnpinned for Unmovable { 149 | //! # type PinData = (); 150 | //! # unsafe fn from_unpinned(s: String) -> (Self, ()) { 151 | //! # ( 152 | //! # Self { 153 | //! # s, 154 | //! # slice: NonNull::dangling(), 155 | //! # _pinned: PhantomPinned, 156 | //! # }, 157 | //! # (), 158 | //! # ) 159 | //! # } 160 | //! # unsafe fn on_pin(&mut self, _data: ()) { 161 | //! # self.slice = NonNull::from(&self.s); 162 | //! # } 163 | //! # } 164 | //! # impl Unmovable { 165 | //! # fn new_unpinned>(s: T) -> Unpinned { 166 | //! # Unpinned::new(s.into()) 167 | //! # } 168 | //! # } 169 | //! use stackpin::stack_let; 170 | //! // ... 171 | //! stack_let!(unmovable = Unmovable::new_unpinned("Intel the Beagle")); // this creates the unmovable instance on the stack and binds `unmovable` with a `PinStack` 172 | //! // ... 173 | //! ``` 174 | //! 175 | //! [pin-utils]: https://docs.rs/pin-utils 176 | //! [`StackPinned`]: struct.StackPinned.html 177 | //! [`StackPinned`]: struct.StackPinned.html 178 | //! [`FromUnpinned`]: trait.FromUnpinned.html 179 | //! [`stack_let`]: macro.stack_let.html 180 | //! [`PinStack`]: type.PinStack.html 181 | //! [`PinStack`]: type.PinStack.html 182 | //! [`Unpinned`]: struct.Unpinned.html 183 | //! [`!Unpin`]: https://doc.rust-lang.org/std/pin/index.html#unpin 184 | 185 | use std::marker::PhantomData; 186 | use std::ops::Deref; 187 | use std::ops::DerefMut; 188 | use std::pin::Pin; 189 | 190 | /// Struct that represents data that is pinned to the stack, at the point of declaration. 191 | /// 192 | /// Because this property cannot be guaranteed by safe rust, constructing an instance of a 193 | /// [`StackPinned`] directly is `unsafe`. 194 | /// Rather, one should use the [`stack_let`] macro that returns a [`PinStack`] instance. 195 | /// 196 | /// In particular, one should note the following about [`StackPinned`] instance: 197 | /// * It is impossible to safely pass a [`StackPinned`] instance to a function 198 | /// * It is impossible to safely return a [`StackPinned`] instance from a function 199 | /// * It is impossible to safely store a [`StackPinned`] instance inside of a struct 200 | /// 201 | /// Instead, one should replace [`StackPinned`] with [`PinStack`] in each of these situations. 202 | /// 203 | /// A [`PinStack`] instance does have its benefits: 204 | /// * The underlying `T` instance is guaranteed to never move for `T: !Unpin` once pinned. 205 | /// This is useful for `T` types whose instances should never move. 206 | /// * For `T: !Unpin`, the destructor of `T` is guaranteed to run when the T leaves the stack frame it was allocated on, 207 | /// even if one uses [`std::mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html) on 208 | /// the [`PinStack`] instance. 209 | /// 210 | /// [`StackPinned`]: struct.StackPinned.html 211 | /// [`StackPinned`]: struct.StackPinned.html 212 | /// [`PinStack`]: type.PinStack.html 213 | /// [`PinStack`]: type.PinStack.html 214 | /// [`stack_let`]: macro.stack_let.html 215 | #[repr(transparent)] 216 | pub struct StackPinned<'pin, T>(&'pin mut T); 217 | 218 | impl<'pin, T> StackPinned<'pin, T> { 219 | /// # Safety 220 | /// Currently the only way to build a safe [`StackPinned`] instance is to use the 221 | /// [`stack_let`] macro that will return a [`PinStack`] instance. 222 | /// 223 | /// [`StackPinned`]: struct.StackPinned.html 224 | /// [`PinStack`]: type.PinStack.html 225 | /// [`stack_let`]: macro.stack_let.html 226 | #[inline(always)] 227 | pub unsafe fn new(t: &'pin mut T) -> Self { 228 | Self(t) 229 | } 230 | } 231 | 232 | impl<'pin, T> Deref for StackPinned<'pin, T> { 233 | type Target = T; 234 | fn deref(&self) -> &Self::Target { 235 | &self.0 236 | } 237 | } 238 | 239 | impl<'pin, T> DerefMut for StackPinned<'pin, T> { 240 | fn deref_mut(&mut self) -> &mut ::Target { 241 | &mut self.0 242 | } 243 | } 244 | 245 | /// Trait to build [`StackPinned`] values from unpinned types. 246 | /// 247 | /// Implementers of `FromUnpinned` indicate that they can be built from a `Source` instance, 248 | /// to the condition that they will be pinned afterwards. 249 | /// 250 | /// # Safety 251 | /// 252 | /// This trait both exposes unsafe functions **and** is unsafe to implement. 253 | /// * Unsafe functions are exposed because the functions have the preconditions of having to be 254 | /// called from the [`stack_let`] macro. 255 | /// * The trait itself is unsafe to implement because implementers must provide implementations of 256 | /// the functions that must uphold invariants that cannot be checked by the compiler. See the 257 | /// documentation of each function for information on the invariants. 258 | /// 259 | /// [`stack_let`]: macro.stack_let.html 260 | /// [`StackPinned`]: struct.StackPinned.html 261 | pub unsafe trait FromUnpinned 262 | where 263 | Self: Sized, 264 | { 265 | /// This associated type can be used to retain information between the creation of the instance and its pinning. 266 | /// This allows for some sort of "two-steps initialization" without having to store the initialization part in the 267 | /// type itself. 268 | type PinData; 269 | 270 | /// Performs a first initialization step, resulting in the creation of the `Self` instance. 271 | /// 272 | /// # Safety 273 | /// 274 | /// * This function is used by the construction macro, it is never safe to call directly. 275 | /// * Implementers of this function are **not** allowed to consider that the type won't ever move **yet**. 276 | /// (in particular, the `Self` instance is returned by this function). The type should be 277 | /// movable at this point. 278 | unsafe fn from_unpinned(src: Source) -> (Self, Self::PinData); 279 | 280 | /// Performs a second initialization step, resulting in the pinning of the `Self` instance. 281 | /// 282 | /// # Safety 283 | /// 284 | /// * This function is used by the construction macro, it is never safe to call directly. 285 | /// * Implementers of this function **are** allowed to consider that the type won't move ever again. 286 | /// You can for instance set autoborrows safely in this function. 287 | /// * For convenience, a naked mutable borrow is directly given. 288 | /// Implementers of this function are **not** allowed to move out of this mutable borrow. 289 | unsafe fn on_pin(&mut self, pin_data: Self::PinData); 290 | } 291 | 292 | /// A helper struct around `U` that remembers the `T` destination type. 293 | /// 294 | /// This struct is typically used to build [`PinStack`] values using the [`stack_let`] macro 295 | /// without having to specify the destination type. 296 | /// 297 | /// # Example 298 | /// 299 | /// ``` 300 | /// # use std::marker::PhantomPinned; 301 | /// # use std::ptr::NonNull; 302 | /// # struct Unmovable { 303 | /// # s: String, 304 | /// # slice: NonNull, 305 | /// # _pinned: PhantomPinned, 306 | /// # } 307 | /// # use stackpin::Unpinned; 308 | /// # use stackpin::FromUnpinned; 309 | /// # unsafe impl FromUnpinned for Unmovable { 310 | /// # type PinData = (); 311 | /// # unsafe fn from_unpinned(s: String) -> (Self, ()) { 312 | /// # ( 313 | /// # Self { 314 | /// # s, 315 | /// # slice: NonNull::dangling(), 316 | /// # _pinned: PhantomPinned, 317 | /// # }, 318 | /// # (), 319 | /// # ) 320 | /// # } 321 | /// # unsafe fn on_pin(&mut self, _data: ()) { 322 | /// # self.slice = NonNull::from(&self.s); 323 | /// # } 324 | /// # } 325 | /// use stackpin::stack_let; 326 | /// // Without `Unpinned` 327 | /// fn new_string(s : impl Into) -> String { s.into() } 328 | /// stack_let!(unmovable : Unmovable = new_string("toto")); 329 | /// // With `Unpinned` 330 | /// fn new_unpinned(s : impl Into) -> Unpinned { Unpinned::new(s.into()) } 331 | /// stack_let!(unmovable = new_unpinned("toto")); 332 | /// ``` 333 | /// 334 | /// [`stack_let`]: macro.stack_let.html 335 | /// [`PinStack`]: type.PinStack.html 336 | pub struct Unpinned> { 337 | u: U, 338 | t: std::marker::PhantomData, 339 | } 340 | 341 | unsafe impl> FromUnpinned> for T { 342 | type PinData = >::PinData; 343 | 344 | unsafe fn from_unpinned(src: Unpinned) -> (Self, Self::PinData) { 345 | >::from_unpinned(src.u) 346 | } 347 | 348 | unsafe fn on_pin(&mut self, pin_data: Self::PinData) { 349 | >::on_pin(self, pin_data) 350 | } 351 | } 352 | 353 | impl> Unpinned { 354 | pub fn new(u: U) -> Self { 355 | Self { u, t: PhantomData } 356 | } 357 | } 358 | 359 | #[doc(hidden)] 360 | #[macro_export] 361 | macro_rules! internal_pin_stack { 362 | ($id:ident) => { 363 | // Shadow the original binding so that it can't directly be accessed ever again. 364 | let $id: $crate::PinStack<_> = unsafe { 365 | let $id = $crate::StackPinned::new(&mut $id); 366 | 367 | std::pin::Pin::new_unchecked($id) 368 | }; 369 | }; 370 | (mut $id:ident) => { 371 | // Shadow the original binding so that it can't directly be accessed ever again. 372 | let mut $id: $crate::PinStack<_> = unsafe { 373 | let $id = $crate::StackPinned::new(&mut $id); 374 | 375 | std::pin::Pin::new_unchecked($id) 376 | }; 377 | }; 378 | } 379 | 380 | #[doc(hidden)] 381 | pub unsafe fn write_pinned(source: Source, pdest: *mut Dest) 382 | where 383 | Dest: FromUnpinned, 384 | { 385 | let (dest, data) = FromUnpinned::::from_unpinned(source); 386 | std::ptr::write(pdest, dest); 387 | FromUnpinned::::on_pin(&mut *pdest, data); 388 | } 389 | 390 | #[doc(hidden)] 391 | pub unsafe fn from_unpinned( 392 | source: Unpinned, 393 | ) -> (Dest, Dest::PinData, PhantomData>) 394 | where 395 | Dest: FromUnpinned, 396 | { 397 | let (dest, data) = FromUnpinned::from_unpinned(source); 398 | (dest, data, PhantomData) 399 | } 400 | 401 | #[doc(hidden)] 402 | pub unsafe fn from_source( 403 | source: Source, 404 | ) -> (Dest, Dest::PinData, PhantomData) 405 | where 406 | Dest: FromUnpinned, 407 | { 408 | let (dest, data) = FromUnpinned::from_unpinned(source); 409 | (dest, data, PhantomData) 410 | } 411 | 412 | #[doc(hidden)] 413 | pub unsafe fn on_pin( 414 | pdest: *mut Dest, 415 | data: Dest::PinData, 416 | _source: PhantomData, 417 | ) where 418 | Dest: FromUnpinned, 419 | { 420 | FromUnpinned::::on_pin(&mut *pdest, data); 421 | } 422 | 423 | /// `stack_let!(id = expr)` binds a [`PinStack`] to `id` if `expr` is an expression of type `U` where [`T: FromUnpinned`]. 424 | /// 425 | /// If `expr` is of type [`Unpinned`] for some `U`, then no type annotation is necessary. 426 | /// If `expr` is of type `U` where [`T: FromUnpinned`], use `stack_let!(id : T = expr)`. 427 | /// 428 | /// To bind `id` mutably, use `stack_let!(mut id = expr)`. 429 | /// 430 | /// [`PinStack`]: type.PinStack.html 431 | /// [`T: FromUnpinned`]: trait.FromUnpinned.html 432 | /// [`Unpinned`]: struct.Unpinned.html 433 | #[macro_export] 434 | macro_rules! stack_let { 435 | ($id: ident = $expr: expr) => { 436 | let (mut $id, _stack_data, _stack_phantom) = unsafe { $crate::from_unpinned($expr) }; 437 | unsafe { $crate::on_pin(&mut $id as *mut _, _stack_data, _stack_phantom) } 438 | 439 | $crate::internal_pin_stack!($id); 440 | }; 441 | (mut $id: ident = $expr: expr) => { 442 | let (mut $id, _stack_data, _stack_phantom) = unsafe { $crate::from_unpinned($expr) }; 443 | unsafe { $crate::on_pin(&mut $id as *mut _, _stack_data, _stack_phantom) } 444 | 445 | $crate::internal_pin_stack!(mut $id); 446 | }; 447 | ($id: ident : $type:ty = $expr: expr) => { 448 | let (mut $id, _stack_data, _stack_phantom) = 449 | unsafe { $crate::from_source::<$type, _>($expr) }; 450 | unsafe { $crate::on_pin(&mut $id as *mut _, _stack_data, _stack_phantom) } 451 | 452 | $crate::internal_pin_stack!($id); 453 | }; 454 | (mut $id: ident : $type:ty = $expr: expr) => { 455 | let (mut $id, _stack_data, _stack_phantom) = 456 | unsafe { $crate::from_source::<$type, _>($expr) }; 457 | unsafe { $crate::on_pin(&mut $id as *mut _, _stack_data, _stack_phantom) } 458 | 459 | $crate::internal_pin_stack!(mut $id); 460 | }; 461 | } 462 | 463 | /// Short-hand for `Pin>` 464 | pub type PinStack<'a, T> = Pin>; 465 | 466 | #[cfg(test)] 467 | mod tests { 468 | use super::FromUnpinned; 469 | use super::PinStack; 470 | use super::Unpinned; 471 | use std::marker::PhantomPinned; 472 | use std::ptr::NonNull; 473 | 474 | struct Unmovable { 475 | data: String, 476 | slice: NonNull, 477 | _pin: PhantomPinned, 478 | } 479 | 480 | impl Unmovable { 481 | fn slice(&self) -> &str { 482 | unsafe { self.slice.as_ref() } 483 | } 484 | 485 | fn slice_mut<'a>(this: &'a mut PinStack) -> &'a mut str { 486 | unsafe { this.as_mut().get_unchecked_mut().slice.as_mut() } 487 | } 488 | } 489 | 490 | impl Unmovable { 491 | fn new_unpinned(src: String) -> Unpinned { 492 | Unpinned::new(src) 493 | } 494 | } 495 | 496 | unsafe impl FromUnpinned for Unmovable { 497 | type PinData = (); 498 | unsafe fn from_unpinned(src: String) -> (Self, Self::PinData) { 499 | ( 500 | Self { 501 | data: src, 502 | slice: NonNull::dangling(), 503 | _pin: PhantomPinned, 504 | }, 505 | (), 506 | ) 507 | } 508 | 509 | unsafe fn on_pin(&mut self, _pin_data: Self::PinData) { 510 | self.slice = NonNull::from(&self.data); 511 | } 512 | } 513 | 514 | #[test] 515 | fn let_stack_unmovable() { 516 | let test_str = "Intel the Beagle is the greatest dog in existence"; 517 | stack_let!(mut unmovable = Unmovable::new_unpinned(String::from(test_str))); 518 | let slice = Unmovable::slice_mut(&mut unmovable); 519 | slice.make_ascii_uppercase(); 520 | assert_eq!(test_str.to_ascii_uppercase(), Unmovable::slice(&unmovable)); 521 | } 522 | } 523 | --------------------------------------------------------------------------------