在Rust中我想给一个类命名,这个类拥有这个名字。
有时名称由String
传递。对于这种情况,我可以简单地移动所有权。
但有时这个名字是由静态字符串(&str
)给出的。
在这种情况下,我想引用该字符串,而不是从中创建String
。
我的问题是:如何在班级中声明此名称字段? 它应该是什么类型的?
有关要求的一些更新/背景:
String
和&str
的原因是我希望将动态分配减少到最小。答案 0 :(得分:3)
一种选择是将名称成员声明为枚举,可以包含String
或&'static str
:
enum Name {
Static(&'static str),
Owned(String),
}
struct Class {
name: Name,
// ...
}
然后,类可以提供适当的构造函数(必须有两个)和get_name()
方法来访问名称作为字符串切片:
impl Class {
pub fn new_from_str(name: &'static str) -> Class {
Class { name: Name::Static(name) }
}
pub fn new_from_owned(name: String) -> Class {
Class { name: Name::Owned(name) }
}
pub fn get_name(&self) -> &str {
match self.name {
Name::Owned(ref s) => s.as_str(),
Name::Static(s) => s,
}
}
}
fn main() {
let c1 = Class::new_from_str("foo");
let c2 = Class::new_from_owned("foo".to_string());
println!("{} {}", c1.get_name(), c2.get_name());
}
另一种选择是使用标准库提供的Cow
type来实现此目的:
use std::borrow::Cow;
struct Class {
name: Cow<'static, str>,
}
由于Cow
实现了Into
特征,现在可以将构造函数编写为单个泛型函数:
pub fn new<T>(name: T) -> Class
where T: Into<Cow<'static, str>> {
Class { name: name.into() }
}
Cow
还实现Deref
特征,允许get_name()
写成:
pub fn get_name(&self) -> &str {
return &self.name;
}
在这两种情况下,name
成员将等于较大变体的大小加上鉴别器所占用的空间。由于此处String
是较大的类型,并且它占用三个指针大小(字符串内容分别分配且不计数),Name
将总共采用四个指针大小。在显式enum
的情况下,可以通过装箱字符串使成员变小:
enum Name {
Static(&'static str),
Owned(Box<String>),
}
这会将Name
的大小减少到三个指针大小,其中一个时隙用于鉴别器,其余两个用于字符串切片。缺点是它需要为拥有字符串的情况进行额外的分配和间接 - 但如果大多数类名来自静态字符串切片,它仍然可能会得到回报。
答案 1 :(得分:0)
允许混合使用类型的方法是使用Into
特征。这是多功能的,因为它确保在类型之间发生安全转换。
可以将str
切片转换为&#34;拥有String
本身。
一些测试代码来演示它:
#[derive(Debug)]
struct Test {
name: String,
}
impl Test {
pub fn new<T: Into<String>>(t: T) -> Test {
Test { name: t.into() }
}
}
fn main() {
let t = Test::new("a");
println!("{:?}", t);
}