string - Rust 创建带有指针偏移的字符串
问题描述
所以假设我有一个String
,"Foo Bar"
并且我想创建一个子字符串"Bar"
而不分配新内存。
所以我将原始字符串的原始指针移动到子字符串的开头(在这种情况下将其偏移 4)并使用 String::from_raw_parts() 函数来创建字符串。
到目前为止,我有以下代码,据我所知,它应该可以做到这一点。我只是不明白为什么这不起作用。
use std::mem;
fn main() {
let s = String::from("Foo Bar");
let ptr = s.as_ptr();
mem::forget(s);
unsafe {
// no error when using ptr.add(0)
let txt = String::from_raw_parts(ptr.add(4) as *mut _, 3, 3);
println!("{:?}", txt); // This even prints "Bar" but crashes afterwards
println!("prints because 'txt' is still in scope");
}
println!("won't print because 'txt' was dropped",)
}
我在 Windows 上收到以下错误:
error: process didn't exit successfully: `target\debug\main.exe` (exit code: 0xc0000374, STATUS_HEAP_CORRUPTION)
这些在 Linux 上(cargo run;cargo run --release):
munmap_chunk(): invalid pointer
free(): invalid pointer
我认为它与String的析构函数有关,因为只要txt
在范围内,程序就可以正常运行。
需要注意的另一件事是,当我使用ptr.add(0)
而不是ptr.add(4)
它运行时没有错误。
另一方面,创建切片并没有给我带来任何问题。丢弃效果很好。
let t = slice::from_raw_parts(ptr.add(4), 3);
String
最后,我想在不分配新内存的情况下将拥有的 String 拆分为多个拥有的 s。
任何帮助表示赞赏。
解决方案
错误的原因是分配器的工作方式。要求分配器释放一个它一开始没有给你的指针是未定义的行为。在这种情况下,分配器为第一个分配了 7 个字节s
并返回了一个指针。然而,当txt
被删除时,它告诉分配器释放一个指向字节 4 的指针,这是它以前从未见过的。这就是为什么当你add(0)
而不是add(4)
.
正确使用unsafe
是困难的,你应该尽可能避免它。
该类型的部分目的&str
是允许string
共享拥有的部分,因此我强烈建议您尽可能使用它们。
如果您不能单独使用的原因&str
是因为您无法将生命周期追溯到原始String
,那么仍然有一些解决方案,但需要权衡取舍:
泄漏内存,因此它实际上是静态的:
let mut s = String::from("Foo Bar"); let s = Box::leak(s.into_boxed_str()); let txt: &'static str = &s[4..]; let s: &'static str = &s[..4];
显然,您只能在应用程序中执行此操作几次,否则您将使用太多无法取回的内存。
使用引用计数来确保原始数据
String
保留足够长的时间以使所有切片都保持有效。这是一个草图解决方案:use std::{fmt, ops::Deref, rc::Rc}; struct RcStr { rc: Rc<String>, start: usize, len: usize, } impl RcStr { fn from_rc_string(rc: Rc<String>, start: usize, len: usize) -> Self { RcStr { rc, start, len } } fn as_str(&self) -> &str { &self.rc[self.start..self.start + self.len] } } impl Deref for RcStr { type Target = str; fn deref(&self) -> &str { self.as_str() } } impl fmt::Display for RcStr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self.as_str(), f) } } impl fmt::Debug for RcStr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self.as_str(), f) } }
fn main() { let s = Rc::new(String::from("Foo Bar")); let txt = RcStr::from_rc_string(Rc::clone(&s), 4, 3); let s = RcStr::from_rc_string(Rc::clone(&s), 0, 4); println!("{:?}", txt); // "Bar" println!("{:?}", s); // "Foo " }
推荐阅读
- javascript - 使用 sinon 存根 yield* 函数调用
- gatsby - 当我运行 gatsby build 时,gatsby js 有模糊的图像
- python - 隐藏 Python 测验的答案
- python - 在 Pygame 中停止多个键盘输入
- sql - 将 Varchar2 转换为 Date 或 Time_Stamp 无效月份?
- r - 如何将多个模型的系数组合到一个整齐的表格中
- java - Zuul 代理 - 如何根据请求路径将请求转发到服务
- python - 如何在 Python 中从 firebase 检索集合中的所有文档
- mongodb - 将图像文件从 MongoDB 传输到 S3
- scala - 使用scala和spark将重复值合并到Dataframe中的字段中