如何在安全的 Rust 中为未确定大小的类型及其拥有的对应类型(如 `str` 和 `String`)创建新类型?
How can I create newtypes for an unsized type and its owned counterpart (like `str` and `String`) in safe Rust?
我想创建一对新类型 Tag(str)
和 TagBuf(String)
,
类似于 Path
和 PathBuf
如何包装 OsStr
和 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));
}
但我 运行 遇到了问题,因为 Tag
是动态调整大小的。
具体来说,implementing Borrow<Tag> for TagBuf
is tricky:
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)
}
}
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
我可以 return 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));
}
…这行得通,我能理解(好!),但它仍然使用
unsafe
对于那个演员,我想避免。
我看到了the Rustonomicon section on exotically sized types,
不使用unsafe
,但是unsizing coercion看起来很复杂,而且
我不知道如何将它从 [u8]
调整为 str
,因为没有
[u8; N]
.
的字符串对应
我也看了Rc<str>
的实现,好像多了一些
通过 Rc<[u8]>
和一些专业魔法进行不安全转换
我听不懂。
我已经阅读了一些相关问题,例如:
- Implementing FromStr for a custom &[u8] type
- https://users.rust-lang.org/t/how-to-newtype-string/5211
...但我还没有找到答案。
最新稳定的 Rust 是否有办法为 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));
}
另请参阅:
我想创建一对新类型 Tag(str)
和 TagBuf(String)
,
类似于 Path
和 PathBuf
如何包装 OsStr
和 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));
}
但我 运行 遇到了问题,因为 Tag
是动态调整大小的。
具体来说,implementing Borrow<Tag> for TagBuf
is tricky:
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)
}
}
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
我可以 return 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));
}
…这行得通,我能理解(好!),但它仍然使用
unsafe
对于那个演员,我想避免。
我看到了the Rustonomicon section on exotically sized types,
不使用unsafe
,但是unsizing coercion看起来很复杂,而且
我不知道如何将它从 [u8]
调整为 str
,因为没有
[u8; N]
.
我也看了Rc<str>
的实现,好像多了一些
通过 Rc<[u8]>
和一些专业魔法进行不安全转换
我听不懂。
我已经阅读了一些相关问题,例如:
- Implementing FromStr for a custom &[u8] type
- https://users.rust-lang.org/t/how-to-newtype-string/5211
...但我还没有找到答案。
最新稳定的 Rust 是否有办法为 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));
}
另请参阅: