如何将一个字符串转换为 &'static str

194

我该如何将一个String转换为&str?更具体地说,我想将它转换为一个具有static生命周期的str&'static str)。


这似乎既不可能也不可取。 'static 生命周期意味着字符串永远不会被释放,即存在内存泄漏的风险。为什么您需要 &'static str 而不是某个适当的 'a&'a str - user395760
3
将其转换为 &'a str ,会是什么样子? - Christoph
通过 as_slice。如果您描述您正在尝试解决的具体问题以及在解决问题时遇到的问题,那么帮助您将会更加容易。 - user395760
还要注意SendStr,这是一种类型,可以是拥有的字符串或静态字符串。 - Chris Morgan
4个回答

254

更新至 Rust 1.0

你不能从一个 String 得到一个 &'static str,因为 String 可能无法在程序的整个生命周期内存在,而这就是 &'static 生命周期的意义。你只能从它得到一个由 String 拥有生命周期参数化的切片。

要从 String 转换成切片 &'a str,可以使用切片语法:

let s: String = "abcdefg".to_owned();
let s_slice: &str = &s[..];  // take a full slice of the string

另外,您可以利用 String 实现 Deref<Target=str> 的事实,并执行显式的重新借用:

let s_slice: &str = &*s;  // s  : String 
                          // *s : str (via Deref<Target=str>)
                          // &*s: &str

甚至还有一种更加简洁的语法,但只能在编译器能够确定所需目标类型的情况下使用(例如在函数参数或显式类型变量绑定中)。它被称为 deref coercion,允许仅使用 & 运算符,并且编译器会根据上下文自动插入适当数量的 *

let s_slice: &str = &s;  // okay

fn take_name(name: &str) { ... }
take_name(&s);           // okay as well

let not_correct = &s;    // this will give &String, not &str,
                         // because the compiler does not know
                         // that you want a &str

请注意,这种模式不仅适用于String/&str - 您可以将其与通过Deref连接的每对类型一起使用,例如来自std::ffi模块的CString/CStrOsString/OsStr或来自std.path模块的PathBuf/Path

66
在 Rust 1.10 中,你可以使用 s.as_str() 来代替 let s_slice: &str = &s[..];。这样做不仅更加简洁易懂,而且不会改变原有代码的含义。 - Shnatsel
7
有时,原始字符串不够长久存在,比如在match {...}块中。这会导致一个's' does not live long enough error的错误。 - Dereckson
7
关于如何在 &strString&[u8]Vec<u8> 之间进行转换的更一般性的回答可以在这里找到:链接 - Anonyme2000
这个答案非常完美。 - Alberto Ruiz

105
你可以这样做,但它涉及泄漏String的内存。这不是你应该轻易做的事情。通过泄漏String的内存,我们保证该内存永远不会被释放(因此泄漏)。因此,对内部对象的任何引用都可以解释为具有“static”生命周期。
fn string_to_static_str(s: String) -> &'static str {
    Box::leak(s.into_boxed_str())
}

fn main() {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).unwrap();
    let s: &'static str = string_to_static_str(s);
}

1
String 在其 API 中并不保证其指向的内存一定在堆上(据我所知,如果我错了请纠正我)。实际上,这可能是可以接受的,但你无法证明你不会使用已经被释放的内存,例如可能在栈上的内存。 - George Hilliard
12
String 保证只要对象没有被丢弃,内存就会一直存在。由于 mem::forget 保证对象永远不会被丢弃,我们可以确保所包含的 str 的引用永远不会无效。因此,我们可以断言它是一个 'static 引用。 - oli_obk
3
这对我的 Rust 应用程序非常有帮助,因为我需要将一个 String 转换成一个 &'static str,以便从原始的 String 创建的令牌可以在所有线程中使用。如果没有这个转换,Rust 编译器会抱怨我的 String 生命周期结束于主函数的结尾,这是不够好的,因为它没有 'static 保证。 - user3704639
3
如果你将整个应用程序放入交叉束范围内,并在范围外创建字符串,你将得到完全相同的结果。 - oli_obk
1
这个答案太好了!它不仅告诉我如何狡猾地创建静态字符串切片,而且让我放弃这种做法!我选择重构我的应用程序,不在那么多地方使用静态字符串切片。 - Paul Chernoch
显示剩余3条评论

46

从Rust 1.26版本开始,可以将String转换为&'static str而不使用unsafe代码:

fn string_to_static_str(s: String) -> &'static str {
    Box::leak(s.into_boxed_str())
}

这将把String实例转换为封装的str并立即泄漏它。这将释放字符串当前可能占用的所有多余容量。

请注意,几乎总有比泄漏对象更好的解决方案,例如如果您想在线程之间共享状态,则使用crossbeam crate。


5
我遇到的一个使用场景涉及到 clap 库。我正在创建一个 arg(命令行参数),指定要使用多少个线程,其默认值等于用户机器上的逻辑核心数。Arg::default_value 接受一个 &str 并仅保留此引用,通常情况下这很好,因为通常该值是编译时常量。但在这种情况下不是这样,这意味着我需要在运行时创建一个 'static 字符串切片(&'static str)。这正是解决我的问题的方法。 - cyqsimon

6

TL;DR:你可以从具有 'static 生命周期的 String 中获取 &'static str

虽然其他答案是正确和最有用的,但有一种(不太有用的)边缘情况,你确实可以将一个 String 转换为 &'static str

引用的生命周期必须始终短于或等于所引用的对象的生命周期。也就是说,所引用的对象必须比引用本身活得更长(或者同样长)。因为 'static 意味着整个程序的生命周期,不存在更长的生命周期。但是相等的生命周期就足够了。因此,如果一个 String 具有 'static 生命周期,则可以从中获取 &'static str 引用。

使用 Rust 1.31 发布的 const fn 特性,理论上已经可以创建类型为 Stringstatic。不幸的是,目前唯一返回 String 的常量函数是 String::new(),并且它仍然在特性门限之后(因此现在需要 Rust 夜版)。

因此,以下代码执行所需的转换(使用夜版)……实际上除了展示在这种边缘情况下可能性的完整性外,没有任何实际用途。

#![feature(const_string_new)]

static MY_STRING: String = String::new();

fn do_something(_: &'static str) {
    // ...
}

fn main() {
    do_something(&MY_STRING);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接