何时以及为何使用AsRef<T>而不是&T

AsRef 文档写入

用于进行廉价的引用到引用转换。

我明白reference-to-reference部分是什么意思cheap?我希望它与复杂性理论(大哦,等等)“便宜”无关。

例子:

struct User {
  email: String, 
  age: u8,
}

impl AsRef<User> for User {
  fn as_ref(&self) -> &User {
     &self
  }
}

fn main() {
  let user = User { email: String::from("myemail@example.com"), age: 25 };
  let user_ref = &user;
  //...
}

什么是执行的原因AsRefUser,如果我可以随便拿一个参考&user?执行的规则是AsRef什么?

PS:我在其他论坛和文档中找不到任何可以回答这些问题的内容。

回答

正如您所指出的,impl AsRef<User> for User似乎有点毫无意义,因为您可以只做&user. 你可以做impl AsRef<String> for Userimpl AsRef<u8> for User作为替代品&user.email&user.age但这些例子可能是对特征的滥用。能够将 a 转换为 an是什么意思?是他们的电子邮件、他们的名字、他们的姓氏、他们的密码吗?它没有多大意义,并且在 a拥有多个领域时分崩离析。User&String&StringUserString

假设我们开始编写一个应用程序,我们只有User电子邮件和年龄。我们会像这样在 Rust 中建模:

struct User {
    email: String,
    age: u8,
}

假设一段时间过去了,我们编写了一堆函数,我们的应用程序变得非常流行,我们决定允许用户成为版主,版主可以拥有不同的版主权限。我们可以这样建模:

struct User {
    email: String,
    age: u8,
}

enum Privilege {
    // imagine different moderator privileges here
}

struct Moderator {
    user: User,
    privileges: Vec<Privilege>,
}

现在我们可以privileges直接将向量添加到User结构体中,但由于少于 1% 的Users 将是Moderators,因此向每个User. 添加的Moderator类型会导致我们写略显尴尬的代码,但因为我们所有的功能仍然采取User如此的,我们必须通过&moderator.user对他们说:

#[derive(Default)]
struct User {
    email: String,
    age: u8,
}

enum Privilege {
    // imagine different moderator privileges here
}

#[derive(Default)]
struct Moderator {
    user: User,
    privileges: Vec<Privilege>,
}

fn takes_user(user: &User) {}

fn main() {
    let user = User::default();
    let moderator = Moderator::default();
    
    takes_user(&user);
    takes_user(&moderator.user); // awkward
}

如果我们可以传递&moderator给任何需要 an 的函数,那就太好了,&User因为版主实际上只是具有一些附加权限的用户。有了AsRef我们可以!这是我们如何实现的:

#[derive(Default)]
struct User {
    email: String,
    age: u8,
}

// obviously
impl AsRef<User> for User {
    fn as_ref(&self) -> &User {
        self
    }
}

enum Privilege {
    // imagine different moderator privileges here
}

#[derive(Default)]
struct Moderator {
    user: User,
    privileges: Vec<Privilege>,
}

// since moderators are just regular users
impl AsRef<User> for Moderator {
    fn as_ref(&self) -> &User {
        &self.user
    }
}

fn takes_user<U: AsRef<User>>(user: U) {}

fn main() {
    let user = User::default();
    let moderator = Moderator::default();
    
    takes_user(&user);
    takes_user(&moderator); // yay
}

现在我们可以将 a 传递&Moderator给任何&User需要 a 的函数,它只需要一个小的代码重构。此外,这种模式现在可以扩展到任意多种用户类型,我们可以添加Admins 和PowerUsers 和SubscribedUsers,只要我们AsRef<User>为它们实现,它们将适用于我们的所有功能。

之所以&Moderator&User开箱的没有我们需要写一个明确的impl AsRef<User> for &Moderator是,因为这个通用毯执行标准库:

impl<T: ?Sized, U: ?Sized> AsRef<U> for &T
where
    T: AsRef<U>,
{
    fn as_ref(&self) -> &U {
        <T as AsRef<U>>::as_ref(*self)
    }
}

这基本上是说,如果我们有一些impl AsRef<U> for T我们还可以自动获得impl AsRef<U> for &T所有T免费的。


以上是何时以及为何使用AsRef&lt;T&gt;而不是&amp;T的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>