在 Rust 中,我想给一个类起一个名字,这个类拥有这个名字。
有时名称由 String
传递。对于这种情况,我可以简单地移动所有权。
但有时这个名称是由静态字符串(&str
)给出的。
对于这种情况,我想引用该字符串,而不是从中创建一个 String
。
我的问题是:如何在我的类中声明这个名称字段? 应该是什么类型?
关于要求的一些更新/背景:
- 名称将是不可变的。
- 之所以要区分一个
String
和一个&str
,是因为我想把动态分配减到最少。
最佳答案
一个选项是将名称成员声明为枚举,它可以包含 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
的大小缩减为三个指针大小,其中一个槽用于鉴别器,其余两个用于字符串切片。缺点是它需要为自有字符串情况进行额外的分配和间接访问 - 但如果您的大部分类名来自静态字符串切片,它仍然可能会有所返回。
关于rust - 如果它的生命周期是静态的,我如何通过引用拥有一个字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40728768/