Rust 如何在稳定状态下分配原始可变指针?
我试图用小字符串优化构建一个自定义Rust 如何在稳定状态下分配原始可变指针?,rust,heap,dynamic-allocation,Rust,Heap,Dynamic Allocation,我试图用小字符串优化构建一个自定义String-like结构的简单实现。现在,允许在稳定锈蚀中使用接头,我提出了以下代码: struct Large { capacity: usize, buffer: *mut u8, } struct Small([u8; 16]); union Container { large: Large, small: Small, } struct MyString { len: usize, containe
String
-like结构的简单实现。现在,允许在稳定锈蚀中使用接头,我提出了以下代码:
struct Large {
capacity: usize,
buffer: *mut u8,
}
struct Small([u8; 16]);
union Container {
large: Large,
small: Small,
}
struct MyString {
len: usize,
container: Container,
}
我似乎找不到一种方法来分配*mut u8
。有可能在稳定的锈蚀中进行吗?看起来使用alloc::heap
是可行的,但它只在夜间可用。怎么样
但很难从代码片段中判断这是否是您真正需要的。您可能需要一个真正的分配器,并且可以与as
cast一起使用,如中所示。有一个提供稳定分配API的。它通过使用Vec::with_capacity
分配内存,然后提取指针来实现:
let vec = Vec::with_capacity(cap);
let ptr = buf.as_mut_ptr();
mem::forget(vec);
要释放内存,请使用从Rust 1.28开始的“原始”零件中的Vec::from\u稳定
下面是一个示例,它大体上说明了如何使用它
use std::{
alloc::{self, Layout},
cmp, mem, ptr, slice, str,
};
// This really should **not** be copied
#[derive(Copy, Clone)]
struct Large {
capacity: usize,
buffer: *mut u8,
}
// This really should **not** be copied
#[derive(Copy, Clone, Default)]
struct Small([u8; 16]);
union Container {
large: Large,
small: Small,
}
struct MyString {
len: usize,
container: Container,
}
impl MyString {
fn new() -> Self {
MyString {
len: 0,
container: Container {
small: Small::default(),
},
}
}
fn as_buf(&self) -> &[u8] {
unsafe {
if self.len <= 16 {
&self.container.small.0[..self.len]
} else {
slice::from_raw_parts(self.container.large.buffer, self.len)
}
}
}
pub fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(self.as_buf()) }
}
// Not actually UTF-8 safe!
fn push(&mut self, c: u8) {
unsafe {
use cmp::Ordering::*;
match self.len.cmp(&16) {
Less => {
self.container.small.0[self.len] = c;
}
Equal => {
let capacity = 17;
let layout = Layout::from_size_align(capacity, mem::align_of::<u8>())
.expect("Bad layout");
let buffer = alloc::alloc(layout);
{
let buf = self.as_buf();
ptr::copy_nonoverlapping(buf.as_ptr(), buffer, buf.len());
}
self.container.large = Large { capacity, buffer };
*self.container.large.buffer.offset(self.len as isize) = c;
}
Greater => {
let Large {
mut capacity,
buffer,
} = self.container.large;
capacity += 1;
let layout = Layout::from_size_align(capacity, mem::align_of::<u8>())
.expect("Bad layout");
let buffer = alloc::realloc(buffer, layout, capacity);
self.container.large = Large { capacity, buffer };
*self.container.large.buffer.offset(self.len as isize) = c;
}
}
self.len += 1;
}
}
}
impl Drop for MyString {
fn drop(&mut self) {
unsafe {
if self.len > 16 {
let Large { capacity, buffer } = self.container.large;
let layout =
Layout::from_size_align(capacity, mem::align_of::<u8>()).expect("Bad layout");
alloc::dealloc(buffer, layout);
}
}
}
}
fn main() {
let mut s = MyString::new();
for _ in 0..32 {
s.push(b'a');
println!("{}", s.as_str());
}
}
重新分配虽然有点痛苦;你必须转换回一个Vec
,然后前后跳整个舞蹈
也就是说,您的Large
结构似乎缺少一个长度
,这与容量不同。您可以使用Vec
而不是将其写出。我现在明白了,它在层次结构中有点高
我想知道如果有一个完整的字符串
会不会容易得多,即使它的效率稍低一点,因为长度是双重计算的
union Container {
large: String,
small: Small,
}
另见:
分配单个u8
。OP需要不止一个缓冲区。@interjay这是真的,但问题是如何分配指针,这是一个有效的答案。我希望来自搜索引擎的人会发现这个问题和答案很有用。长度字段在MyString
中,它也包含Large
或Small
@interjay啊,确实。这正是我要找的!非常感谢。不知何故,我没有意识到我可以使用Vec进行分配和解除分配。FWIW,String
本身无法使用SSO,因为它公开了某些方法。您将无法复制完整的API,但希望这不会阻止您的用例。
use std::mem;
fn main() {
let mut foo = vec![0; 1024]; // or Vec::<u8>::with_capacity(1024);
let ptr = foo.as_mut_ptr();
let cap = foo.capacity();
let len = foo.len();
mem::forget(foo); // Avoid calling the destructor!
let foo_again = unsafe { Vec::from_raw_parts(ptr, len, cap) }; // Rebuild it to drop it
// Do *NOT* use `ptr` / `cap` / `len` anymore
}
union Container {
large: String,
small: Small,
}