何时以及为何使用 AsRef<T> 而不是 &T
When and why to use AsRef<T> instead of &T
AsRef
文档写道
Used to do a cheap reference-to-reference conversion.
我明白 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;
//...
}
如果我可以简单地通过&user
作为参考,为什么要为User
实施AsRef
?
AsRef
的执行规则是什么?
PS:我在其他论坛和文档中找不到任何可以回答这些问题的内容。
I hope it has nothing to do with complexity theoretic (big Oh,. etc) "cheapness".
绝对是。 AsRef
旨在不花任何钱。
What is reason to implement AsRef for User if I can take a reference by simply &user?
可能没有。 AsRef
对于通用代码很有用,尤其是(但不限于)人体工程学。
例如,如果 std::fs::rename
拿了一个 &Path
你必须写:
fs::rename(Path::new("a.txt"), Path::new("b.txt"))?;
冗长且烦人。
然而,由于它 确实 采用 AsRef<Path>
而不是它可以使用字符串开箱即用,这意味着您可以调用:
fs::rename("a.txt", "b.txt")?;
非常简单易读。
正如您所指出的,impl AsRef<User> for User
似乎有点毫无意义,因为您可以做到 &user
。您可以使用 impl AsRef<String> for User
或 impl AsRef<u8> for User
作为 &user.email
和 &user.age
的替代方法,但这些示例可能是对特征的滥用。能够将 User
转换为 &String
是什么意思 ? &String
是他们的电子邮件、名字、姓氏和密码吗?它没有多大意义,并且在 User
具有多个 String
字段的那一刻就崩溃了。
假设我们开始编写一个应用程序,我们只有 User
s 包含电子邮件和年龄。我们会像这样在 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
结构中,但由于User
的不到1%将是 Moderator
s 向每个 User
添加向量似乎是在浪费内存。 Moderator
类型的添加导致我们编写的代码有点笨拙,因为我们所有的函数仍然需要 User
s,所以我们必须将 &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
传递给任何需要 &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
}
现在我们可以将 &Moderator
传递给任何需要 &User
的函数,而且只需要进行少量代码重构。此外,这种模式现在可以扩展到任意多的用户类型,我们可以添加 Admin
s 和 PowerUser
s 和 SubscribedUser
s,只要我们为他们实现 AsRef<User>
,他们就会使用我们的所有功能。
为什么 &Moderator
到 &User
开箱即用而不需要我们写一个明确的 impl AsRef<User> for &Moderator
是因为标准库中的 this generic blanket implementation:
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
我们也会自动免费获得所有 T
的 impl AsRef<U> for &T
。
AsRef
文档写道
Used to do a cheap reference-to-reference conversion.
我明白 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;
//...
}
如果我可以简单地通过&user
作为参考,为什么要为User
实施AsRef
?
AsRef
的执行规则是什么?
PS:我在其他论坛和文档中找不到任何可以回答这些问题的内容。
I hope it has nothing to do with complexity theoretic (big Oh,. etc) "cheapness".
绝对是。 AsRef
旨在不花任何钱。
What is reason to implement AsRef for User if I can take a reference by simply &user?
可能没有。 AsRef
对于通用代码很有用,尤其是(但不限于)人体工程学。
例如,如果 std::fs::rename
拿了一个 &Path
你必须写:
fs::rename(Path::new("a.txt"), Path::new("b.txt"))?;
冗长且烦人。
然而,由于它 确实 采用 AsRef<Path>
而不是它可以使用字符串开箱即用,这意味着您可以调用:
fs::rename("a.txt", "b.txt")?;
非常简单易读。
正如您所指出的,impl AsRef<User> for User
似乎有点毫无意义,因为您可以做到 &user
。您可以使用 impl AsRef<String> for User
或 impl AsRef<u8> for User
作为 &user.email
和 &user.age
的替代方法,但这些示例可能是对特征的滥用。能够将 User
转换为 &String
是什么意思 ? &String
是他们的电子邮件、名字、姓氏和密码吗?它没有多大意义,并且在 User
具有多个 String
字段的那一刻就崩溃了。
假设我们开始编写一个应用程序,我们只有 User
s 包含电子邮件和年龄。我们会像这样在 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
结构中,但由于User
的不到1%将是 Moderator
s 向每个 User
添加向量似乎是在浪费内存。 Moderator
类型的添加导致我们编写的代码有点笨拙,因为我们所有的函数仍然需要 User
s,所以我们必须将 &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
传递给任何需要 &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
}
现在我们可以将 &Moderator
传递给任何需要 &User
的函数,而且只需要进行少量代码重构。此外,这种模式现在可以扩展到任意多的用户类型,我们可以添加 Admin
s 和 PowerUser
s 和 SubscribedUser
s,只要我们为他们实现 AsRef<User>
,他们就会使用我们的所有功能。
为什么 &Moderator
到 &User
开箱即用而不需要我们写一个明确的 impl AsRef<User> for &Moderator
是因为标准库中的 this generic blanket implementation:
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
我们也会自动免费获得所有 T
的 impl AsRef<U> for &T
。