mirror of https://github.com/rust-lang/nomicon
				
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							71 lines
						
					
					
						
							2.2 KiB
						
					
					
				
			
		
		
	
	
							71 lines
						
					
					
						
							2.2 KiB
						
					
					
				| # Layout
 | |
| 
 | |
| First off, we need to come up with the struct layout. A Vec has three parts:
 | |
| a pointer to the allocation, the size of the allocation, and the number of
 | |
| elements that have been initialized.
 | |
| 
 | |
| Naively, this means we just want this design:
 | |
| 
 | |
| ```rust
 | |
| pub struct Vec<T> {
 | |
|     ptr: *mut T,
 | |
|     cap: usize,
 | |
|     len: usize,
 | |
| }
 | |
| # fn main() {}
 | |
| ```
 | |
| 
 | |
| And indeed this would compile and work correctly. However it comes with a semantic
 | |
| limitation and a missed optimization opportunity.
 | |
| 
 | |
| In terms of semantics, this implementation of Vec would be [invariant over T][variance].
 | |
| So a `&Vec<&'static str>` couldn't be used where an `&Vec<&'a str>` was expected.
 | |
| 
 | |
| In terms of optimization, this implementation of Vec wouldn't be eligible for the
 | |
| *null pointer optimization*, meaning `Option<Vec<T>>` would take up more space
 | |
| than `Vec<T>`.
 | |
| 
 | |
| These are fairly common problems because the raw pointer types in Rust aren't
 | |
| very well optimized for this use-case. They're more tuned to make it easier to
 | |
| express C APIs. This is why the standard library provides a pointer type that
 | |
| better matches the semantics pure-Rust abstractions want: `Shared<T>`.
 | |
| 
 | |
| Compared to `*mut T`, `Shared<T>` provides three benefits:
 | |
| 
 | |
| * Variant over `T` (dangerous in general, but desirable for collections)
 | |
| * Null-pointer optimizes (so `Option<Shared<T>>` is pointer-sized)
 | |
| 
 | |
| We could get the variance requirement ourselves using `*const T` and casts, but
 | |
| the API for expressing a value is non-zero is unstable, and that isn't expected
 | |
| to change any time soon.
 | |
| 
 | |
| Shared should be stabilized in some form very soon, so we're just going to use
 | |
| that.
 | |
| 
 | |
| 
 | |
| ```rust
 | |
| #![feature(shared)]
 | |
| 
 | |
| use std::ptr::Shared;
 | |
| 
 | |
| pub struct Vec<T> {
 | |
|     ptr: Shared<T>,
 | |
|     cap: usize,
 | |
|     len: usize,
 | |
| }
 | |
| 
 | |
| # fn main() {}
 | |
| ```
 | |
| 
 | |
| If you don't care about the null-pointer optimization, then you can use `*const T`.
 | |
| For most code, using `*mut T` would also be perfectly reasonable.
 | |
| However this chapter is focused on providing an implementation that matches the
 | |
| quality of the one in the standard library, so we will be designing the rest of
 | |
| the code around using Shared.
 | |
| 
 | |
| Lastly, it should be noted that `Shared::new` is unsafe to call, because
 | |
| putting `null` inside of it is Undefined Behavior. Code that doesn't use Shared
 | |
| has no such concern.
 | |
| 
 | |
| [variance]: variance.html
 |