一、智能指针
指针还是引用
引用是特殊的指针
- 指针是一个持有内存地址的值,可以通过解引用来访问它指向的内存地址,理论上可以解引用到任意数据类型;
- 引用是一个特殊 的指针,它的解引用访问是受限的,只能解引用到它引用数据的类型,不能用作它用。
智能指针不仅是指针
智能指针=胖指针+所有权
智能指针和结构体有什么区别
- String用结构体定义,其实就是Vec
#![allow(unused)] fn main() { pub struct String { vec: Vec<u8>, } }
- 和普通的结构体不同的是,String 实现了 Deref 和 DerefMut,这使得它在解引用的时 候,会得到 &str
#![allow(unused)] fn main() { impl ops::Deref for String { type Target = str; fn deref(&self) -> &str { unsafe { str::from_utf8_unchecked(&self.vec) } } } impl ops::DerefMut for String { fn deref_mut(&mut self) -> &mut str { unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) } } } }
- 另外,由于在堆上分配了数据,String 还需要为其分配的资源做相应的回收。而 String 内部使用了 Vec,所以它可以依赖 Vec 的能力来释放堆内存
#![allow(unused)] fn main() { unsafe impl<#[may_dangle] T, A: Allocator> Drop for Vec<T, A> { fn drop(&mut self) { unsafe { // use drop for [T] // use a raw slice to refer to the elements of the vector as weakest necessary type; // could avoid questions of validity in certain cases ptr::drop_in_place(ptr::slice_from_raw_parts_mut(self.as_mut_ptr(), self.len)) } // RawVec handles deallocation } } }
在 Rust 中,凡是需要做资源回收的数据结构,且实现了 Deref/DerefMut/Drop,都是智能指针。
按照这个定义,除了 String,还有很多智能指针,比如:
-
用于在堆上 分配内存的 Box
和 Vec -
用于引用计数的 Rc
和 Arc -
很多其他数据结 构,如 PathBuf、Cow<’a, B>、MutexGuard
、RwLockReadGuard 和 RwLockWriteGuard 等也是智能指针。
自定义智能指针
MyString实现代码
use std::{fmt, ops::Deref, str}; const MINI_STRING_MAX_LEN: usize = 30; // MyString 里,String 有 3 个 word,供 24 字节,所以它以 8 字节对齐 // 所以 enum 的 tag + padding 最少 8 字节,整个结构占 32 字节。 // MiniString 可以最多有 30 字节(再加上 1 字节长度和 1字节 tag),就是 32 字节. struct MiniString { len: u8, data: [u8; MINI_STRING_MAX_LEN], } impl MiniString { // 这里 new 接口不暴露出去,保证传入的 v 的字节长度小于等于 30 fn new(v: impl AsRef<str>) -> Self { let bytes = v.as_ref().as_bytes(); // 我们在拷贝内容时一定要要使用字符串的字节长度 let len = bytes.len(); let mut data = [0u8; MINI_STRING_MAX_LEN]; data[..len].copy_from_slice(bytes); Self { len: len as u8, data, } } } impl Deref for MiniString { type Target = str; fn deref(&self) -> &Self::Target { // 由于生成 MiniString 的接口是隐藏的,它只能来自字符串,所以下面这行是安全的 str::from_utf8(&self.data[..self.len as usize]).unwrap() // 也可以直接用 unsafe 版本 // unsafe { str::from_utf8_unchecked(&self.data[..self.len as usize]) } } } impl fmt::Debug for MiniString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // 这里由于实现了 Deref trait,可以直接得到一个 &str 输出 write!(f, "{}", self.deref()) } } #[derive(Debug)] enum MyString { Inline(MiniString), Standard(String), } // 实现 Deref 接口对两种不同的场景统一得到 &str impl Deref for MyString { type Target = str; fn deref(&self) -> &Self::Target { match *self { MyString::Inline(ref v) => v.deref(), MyString::Standard(ref v) => v.deref(), } } } // impl From<&str> for MyString { // fn from(s: &str) -> Self { // match s.len() > MINI_STRING_MAX_LEN { // true => Self::Standard(s.to_owned()), // _ => Self::Inline(MiniString::new(s)), // } // } // } // impl From<String> for MyString { // fn from(s: String) -> Self { // match s.len() > MINI_STRING_MAX_LEN { // true => Self::Standard(s), // _ => Self::Inline(MiniString::new(s)), // } // } // } impl<T> From<T> for MyString where T: AsRef<str>, { fn from(s: T) -> Self { match s.as_ref().len() > MINI_STRING_MAX_LEN { true => Self::Standard(s.as_ref().to_owned()), _ => Self::Inline(MiniString::new(s)), } } } impl fmt::Display for MyString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.deref()) } } impl MyString { pub fn push_str(&mut self, s: &str) { match *self { MyString::Inline(ref mut v) => { let len = v.len as usize; let len1 = s.len(); if len + len1 > MINI_STRING_MAX_LEN { let mut owned = v.deref().to_string(); owned.push_str(s); *self = MyString::Standard(owned); } else { let total = len + len1; v.data[len..len + len1].copy_from_slice(s.as_bytes()); v.len = total as u8; } } MyString::Standard(ref mut v) => v.push_str(s), } } } fn main() { let len1 = std::mem::size_of::<MyString>(); let len2 = std::mem::size_of::<MiniString>(); println!("Len: MyString {}, MiniString {}", len1, len2); let s1: MyString = "hello world".into(); let s2: MyString = "这是一个超过了三十个字节的很长很长的字符串".into(); // debug 输出 println!("s1: {:?}, s2: {:?}", s1, s2); // display 输出 println!( "s1: {}({} bytes, {} chars), s2: {}({} bytes, {} chars)", s1, s1.len(), s1.chars().count(), s2, s2.len(), s2.chars().count() ); // MyString 可以使用一切 &str 接口,感谢 Rust 的自动 Deref assert!(s1.ends_with("world")); assert!(s2.starts_with('这')); let s = String::from("这是一个超过了三十个字节的很长很长的字符串"); println!("s: {:p}", &*s); // From<T: AsRef<str>> 的实现会导致额外的复制 let s3: MyString = s.into(); println!("s3: {:p}", &*s3); let mut s4: MyString = "Hello Tyr! ".into(); println!("s4: {:?}", s4); s4.push_str("这是一个超过了三十个字节的很长很长的字符串"); println!("s4: {:?}", s4); }
为了让 MyString 表现行为和 &str 一致:
- 我们可以通过实现 Deref trait 让 MyString 可以被解引用成 &str。
- 除此之外,还可以实现 Debug/Display 和 From
trait,让 MyString 使用起来更方便。 - 这个简单实现的 MyString,不管它内部的数据是纯栈上的 MiniString 版本,还是包含堆 上内存的 String 版本,使用的体验和 &str 都一致,仅仅牺牲了一点点效率和内存,就可 以让小容量的字符串,可以高效地存储在栈上并且自如地使用。
- smartstring 的第三方库实现类似功能,还做了优化。