gpt4 book ai didi

rust - 如何在稳定的 Rust 中分配原始可变指针?

转载 作者:行者123 更新时间:2023-11-29 08:10:33 26 4
gpt4 key购买 nike

我试图通过小字符串优化构建自定义 String 类结构的简单实现。现在在稳定的 Rust 中允许联合,我想出了以下代码:

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 的方法。有可能在稳定的 Rust 中做吗?看起来使用 alloc::heap 是可行的,但它只能在夜间使用。

最佳答案

从 Rust 1.28 开始,std::alloc::alloc稳定。

这里是一个示例,展示了如何使用它。

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());
}
}

我相信这段代码在分配方面是正确的,但在其他任何方面都不正确。像所有不安全代码一样,自己验证。它也完全没有效率,因为它会为每个额外的字符重新分配。


如果你想分配一个 u8 的集合而不是单个 u8,你可以创建一个 Vec 然后转换它进入组成部分,例如通过调用 as_mut_ptr :

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
}

重新分配虽然有点痛苦;你必须转换回 Vec 并向前和向后进行整个舞蹈

也就是说,您的 Large 结构似乎缺少一个 length,这与容量不同。您可以只使用 Vec 而不是将其写出来。我现在看到它在层次结构中向上了一点。

我想知道拥有一个完整的 String 是否不会容易很多,即使它的效率有点低,因为长度被重复计算......

union Container {
large: String,
small: Small,
}

另见:

关于rust - 如何在稳定的 Rust 中分配原始可变指针?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45306575/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com