如果生命周期是静态的,我如何通过引用拥有一个字符串?

时间:2016-11-21 20:28:16

标签: rust

在Rust中我想给一个类命名,这个类拥有这个名字。

有时名称由String传递。对于这种情况,我可以简单地移动所有权。

但有时这个名字是由静态字符串(&str)给出的。 在这种情况下,我想引用该字符串,而不是从中创建String

我的问题是:如何在班级中声明此名称字段? 它应该是什么类型的?

有关要求的一些更新/背景:

  • 名称将是不可变的。
  • 我想区分String&str的原因是我希望将动态分配减少到最小。

2 个答案:

答案 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);
}