是否可以专注于静态生命周期?

Is it possible to specialize on a static lifetime?

我想从 &'a str 专攻 &'static str:

use std::borrow::Cow;

struct MyString {
    inner: Cow<'static, str>,
}

impl From<&'static str> for MyString {
    fn from(x: &'static str) -> Self {
        MyString {
            inner: Cow::Borrowed(x),
        }
    }
}

impl<T: Into<String>> From<T> for MyString {
    fn from(x: T) -> Self {
        MyString {
            inner: Cow::Owned(x.into()),
        }
    }
}

fn main() {
    match MyString::from("foo").inner {
        Cow::Borrowed(..) => (),
        _ => {
            panic!();
        }
    }

    let s = String::from("bar");
    match MyString::from(s.as_ref()).inner {
        Cow::Owned(..) => (),
        _ => {
            panic!();
        }
    }

    match MyString::from(String::from("qux")).inner {
        Cow::Owned(..) => (),
        _ => {
            panic!();
        }
    }
}

要点是 MyString 将静态分配的字符串文字存储为 &'static str,将所有其他字符串存储为 String。这允许 MyString 避免使用生命周期参数,即 MyString<'a>,这对我的 API 至关重要,同时允许调用者传入任何类型的字符串并具有 MyString 自动做正确的事。

问题是代码无法编译:

error[E0119]: conflicting implementations of trait `std::convert::From<&'static str>` for type `MyString`:
  --> src/main.rs:15:1
   |
7  | impl From<&'static str> for MyString {
   | ------------------------------------ first implementation here
...
15 | impl<T: Into<String>> From<T> for MyString {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyString`

有什么技巧可以让我做我想做的事吗?如果不是,那么 Rust 会支持终生专业化吗?

Rust 1.51.0 没有任何类型的专门化。如果我正确阅读 the specialization RFC,那么即使实施了 RFC,生命周期专业化也将 得到支持:

A hard constraint in the design of the trait system is that dispatch cannot depend on lifetime information. In particular, we both cannot, and should not allow specialization based on lifetimes:

  • We can't, because when the compiler goes to actually generate code ("trans"), lifetime information has been erased -- so we'd have no idea what specializations would soundly apply.

  • We shouldn't, because lifetime inference is subtle and would often lead to counterintuitive results. For example, you could easily fail to get 'static even if it applies, because inference is choosing the smallest lifetime that matches the other constraints.

(强调我的)

link 中还有一些示例表明了一些具体问题。

我建议使用 Cow 来处理“拥有或借用”的情况。

我在阅读这个 duplicated post 询问如何定义一个 method/function 在传递静态字符串或非静态字符串时表现不同的问题后写了这个答案。

这是不可能的,因此解决方法可能是使用包装器类型将字符串参数包装在 enum:

enum MyString {
    Static(&'static str),
    Heap(String),
}

fn bar(arg: &MyString) {
    match arg {
        &MyString::Static(ref name) => println!("my first pc was {}", name),
        &MyString::Heap(ref name) => println!("I dont know {}", name),
    }
}

fn main() {
    let mut v = Vec::new();

    let forever: &'static str = "zx-spectrum";
    let local: &str = &"commodore64".to_string();

    v.push(MyString::Static(forever));

    // ERROR: try to insert 'a lifetime
    // v.push(Mystring::Static(local));
    v.push(MyString::Heap(local.to_string()));

    v.push(MyString::Heap("muggle".to_string()));

    bar(&v[0]);
    bar(&v[1]);
}

MyString 将静态分配的字符串文字存储为 &'static str,将所有其他字符串存储为 String

正如下面的评论所指出的,标准库提供了一种适合 borrowed/owned 情况的类型:智能指针 Cow.

本例中使用的枚举MyString只是一个用于管理字符串类型的特定枚举。

唯一的区别在于枚举的更具体命名及其与特定用法相关的变体:MyString::Static("forever")Cow::Borrowed("forever")MyString::Heap(str)Cow::Owned(str) .

这是否有助于提高助记符和代码可读性?我很确定这只适用于新手或偶尔的 Rust 程序员,而不适用于经验丰富的 Rustaceans。