我正在阅读有关String
的书籍部分,并发现他们正在使用&*
组合在一起来转换一段文字。以下是它的说法:
use std::net::TcpStream;
TcpStream::connect("192.168.0.1:3000"); // Parameter is of type &str.
let addr_string = "192.168.0.1:3000".to_string();
TcpStream::connect(&*addr_string); // Convert `addr_string` to &str.
换句话说,他们说他们正在将String
转换为&str
。但为什么使用上述两种标志进行转换呢?如果不使用其他方法吗? &
是否意味着我们正在引用它,然后使用*
取消引用它?
答案 0 :(得分:10)
简而言之:*
触发显式deref,可以通过ops::Deref
重载。
看看这段代码:
let s = "hi".to_string(); // : String
let a = &s;
a
的类型是什么?它只是&String
!这不应该是非常令人惊讶的,因为我们引用了String
。好的,但是这个怎么样?
let s = "hi".to_string(); // : String
let b = &*s; // equivalent to `&(*s)`
b
的类型是什么?这是&str
!哇,发生什么事了?
请注意,*s
首先执行。与大多数运算符一样,解除引用运算符*
也是可重载的,运算符的使用可以被认为是*std::ops::Deref::deref(&s)
的语法糖(注意我们在这里递归解除引用!)。 String
重载此运算符:
impl Deref for String {
type Target = str;
fn deref(&self) -> &str { ... }
}
因此,*s
实际上是*std::ops::Deref::deref(&s)
,其中deref()
函数的返回类型为&str
,然后再次取消引用。因此,*s
的类型为str
(请注意缺少&
)。
由于str
未经过简化而且不是非常方便,我们希望改为引用它,即&str
。我们可以通过在表达式前添加&
来实现此目的! Tada,现在我们达到了类型&str
!
&*s
更像是手动和明确的形式。通常,Deref
- 过载通过自动deref强制来使用。修复目标类型后,编译器将为您解析:
fn takes_string_slice(_: &str) {}
let s = "hi".to_string(); // : String
takes_string_slice(&s); // this works!
答案 1 :(得分:8)
通常,&*
表示首先取消引用(*
),然后引用(&
)一个值。在许多情况下,这将是愚蠢的,因为我们最终会做同样的事情。
然而,Rust有deref coercions。结合Deref
和DerefMut
特征,类型可以取消引用不同的类型!
这对于String
很有用,因为这意味着他们可以从str
获取所有方法,它对Vec<T>
有用,因为它从[T]
获取方法,它对所有智能指针都非常有用,比如Box<T>
,它将包含包含T
的所有方法!
关注String
的链:
String --deref--> str --ref--> &str
&
是否意味着我们正在引用它,然后使用*
取消引用它?
不,您的操作顺序是倒退的。 *
和&
与右侧相关联。在此示例中,首先取消引用,然后引用。
我想现在你可以做到这一点
&addr_string
(来自评论)
有时候,这会做同样的事情。有关详细信息,请参阅What are Rust's exact auto-dereferencing rules?,但是,可以将&String
传递给需要&str
的函数。有时你需要手工做这个小舞蹈。我能想到的最常见的是:
let name: Option<String> = Some("Hello".to_string());
let name2: Option<&str> = name.as_ref().map(|s| &**s);
您会注意到我们实际取消引用两次:
&String -->deref--> String --deref--> str --ref--> &str
虽然现在可以使用name.as_ref().map(String::as_str);