我想创建一对新类型Tag(str)
and TagBuf(String)
,类似于 howPath
和PathBuf
wrap OsStr
and OsString
。我的最终目标是拥有一个TagBuf
以 为键的地图,并且能够只用一个索引来索引它Tag
:
fn main() {
let mut m: HashMap<TagBuf, i32> = HashMap::new();
m.insert(TagBuf("x".to_string()), 1);
assert_eq!(m.get(Tag::new("x")), Some(&1));
}
Run Code Online (Sandbox Code Playgroud)
但是我遇到了问题,因为它Tag
是动态大小的。
具体来说,实施起来Borrow<Tag> for TagBuf
很棘手:
pub struct Tag(str);
pub struct TagBuf(String);
impl std::borrow::Borrow<Tag> for TagBuf {
fn borrow(&self) -> &Tag {
let s: &str = self.0.as_str();
// How can I turn `&str` into `&Tag`? A naive attempt fails:
&Tag(*s)
}
}
Run Code Online (Sandbox Code Playgroud)
error[E0277]: the size for values of type `str` cannot be known at compilation time
--> src/lib.rs:8:10
|
8 | &Tag(*s)
| ^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `str`
= note: all function arguments must have a statically known size
Run Code Online (Sandbox Code Playgroud)
我可以只返回unsafe { std::mem::transmute(s) }
一个
#[repr(transparent)]
注释,但我想避免不安全的代码。
我查看了Path
/的来源PathBuf
并得出以下结论:
use std::borrow::Borrow;
use std::ops::Deref;
#[repr(transparent)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Tag(str);
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub struct TagBuf(String);
impl Tag {
fn new<S: AsRef<str> + ?Sized>(s: &S) -> &Tag {
unsafe { &*(s.as_ref() as *const str as *const Tag) }
}
}
impl Deref for TagBuf {
type Target = Tag;
fn deref(&self) -> &Tag {
Tag::new(&self.0)
}
}
impl Borrow<Tag> for TagBuf {
fn borrow(&self) -> &Tag {
self.deref()
}
}
impl ToOwned for Tag {
type Owned = TagBuf;
fn to_owned(&self) -> TagBuf {
TagBuf(self.0.to_owned())
}
}
fn main() {
let mut m = std::collections::HashMap::<TagBuf, i32>::new();
m.insert(TagBuf("x".to_string()), 1);
assert_eq!(m.get(Tag::new("x")), Some(&1));
}
Run Code Online (Sandbox Code Playgroud)
......这有效,我可以理解它(很好!),但它仍然
unsafe
用于那个演员阵容,我想避免这种情况。
我看到了关于异国情调大小类型的 Rustonomicon 部分,它
不使用unsafe
,但不调整大小的强制似乎很复杂,我不知道如何从[u8]
to适应它str
,因为没有对[u8; N]
.
我还阅读了 的实现Rc<str>
,它似乎通过一些更不安全的转换Rc<[u8]>
和一些我难以理解的专业化魔法来进行。
我已经阅读了一些相关的问题,例如:
……但我还没有找到答案。
最新是否稳定的锈有一种方法来定义NEWTYPE对用于str
和String
在安全的代码?如果没有,是否有我应该遵循的 RFC 或跟踪问题?
如果没有一些小的开销,这个问题就无法在安全的 Rust 中解决。
这就是我使用以下方法解决它的方法unsafe
:
use std::{borrow::Borrow, ops::Deref};
#[repr(transparent)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Tag(str);
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub struct TagBuf(String);
impl Tag {
fn new<S: AsRef<str> + ?Sized>(s: &S) -> &Tag {
unsafe { &*(s.as_ref() as *const str as *const Tag) }
}
}
impl Deref for TagBuf {
type Target = Tag;
fn deref(&self) -> &Tag {
Tag::new(&self.0)
}
}
impl Borrow<Tag> for TagBuf {
fn borrow(&self) -> &Tag {
self.deref()
}
}
impl ToOwned for Tag {
type Owned = TagBuf;
fn to_owned(&self) -> TagBuf {
TagBuf(self.0.to_owned())
}
}
use std::collections::HashMap;
fn main() {
let mut m = HashMap::new();
m.insert(TagBuf("x".to_string()), 1);
assert_eq!(m.get(Tag::new("x")), Some(&1));
}
Run Code Online (Sandbox Code Playgroud)
也可以看看:
归档时间: |
|
查看次数: |
224 次 |
最近记录: |