Rust学习之——From Trait和Into Trait以及“类型”到字符串的转换
一次奇怪的實踐
今天看了一個demo,為自定義類型到String類型的轉換實現了From Trait下的from方法,但是是調用into方法來執行該轉換。一開始就覺得很離譜。具體如下所示:
struct OriginType{value: String, }impl From<OriginType> for TargetType {fn from (t: OriginType) -> TargetType {省略} }let o: OriginType = OriginType::new("asdad"); let t: TargetType = o.into();然后查了資料,發現From和Into這兩個Trait是一對,經常在一起出現,但是為什么實現from方法卻可以直接用into調用呢?
于是我提出了如下的幾個問題:
先回答第一個問題
Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into.
One should always prefer implementing From over Into because implementing From automatically provides one with an implementation of Into thanks to the blanket implementation in the standard library.
根據官網的描述,From和Into這兩個Trait是互為相反的(reciprocal)。并且人們應該更喜歡優先實現From,因為實現From會自動提供Into的實現,這要歸功于標準庫中的一攬子(blanket)實現。
第二個問題
Only implement Into when targeting a version prior to Rust 1.41 and converting to a type outside the current crate. From was not able to do these types of conversions in earlier versions because of Rust’s orphaning rules. See Into for more details.
Into Trait只在Rust1.41之前,并且用于把一個類型轉換為當前crate之外的類型(例如標準庫中的Vec或外部庫中的類型)。這是因為Rust有孤兒規則,不能去改動外部的,或者說是當前crate之外的類型,而From在早期的版本中無法做到這些類型之間的轉換。
Prefer using Into over using From when specifying trait bounds on a generic function. This way, types that directly implement Into can be used as arguments as well.
當在泛型函數上指定Trait bounds時,應該優先使用Into。稍后將在Into的用法中詳細介紹。
From Trait
From Trait 用于提供值與值之間的轉換(從類型OriginType到另一個類型TargetType)。
pub trait From<T> {fn from(T) -> Self; }要為TargetType實現From,必須實現的方法是from。看下面的一個例子:
impl From<OriginType> for TargetType {fn from(value: OriginType) -> Self {// pass// 返回一個TargetType的實例} }let o: OriginType; // 這兩種方法都可以,第一種方法可以自動推斷類型,因此不需要類型注解 // 第二種方法由于使用了into方法,而且一個類型可以轉換為多個其他類型,因此必須進行類型注解 let t = TargetType::from(o); let t: TargetType = o.into();實例:下面的代碼片段就是從字符串的HTTP請求 轉換為 為帶有數據結構的、便于查詢的HttpRequest的結構體類型。
impl From<String> for HttpRequest {fn from (req: String) -> Self {return HttpRequest {method: parsed_method(req),resource: parsed_resource(req),version: parsed_version(req),headers: parsed_headers(req),body: parsed_body(req),}} }let req_string: String = String::from_utf8(read_buffer.to_vec()).unwrap(); // 有兩種方法第一種是實現的from方法,第二種是自動對應的Into實現的into方法 // 第一種方法可以自動推斷類型,因此可以不寫類型注解 // 第二種方法使用了into方法,而且一個類型可以轉換為多個其他類型,因此必須進行類型注解 let http_request = HttpRequest::from(req_string); let http_request: HttpRequest = req_string.into();再說Into Trait
之前說了,在Rust1.41之前,如果目標類型不是當前crate中的一部分,那么不能直接實現From方法,例如使用以下的代碼:
在下面的代碼片段中,Wrapper是一個自定義的元組結構體,用于存放類型T的向量,而目標類型Vec<T>是標準庫中的類型。
struct Wrapper<T>(Vec<T>);impl<T> From<Wrapper<T>> for Vec<T> {fn from(w: Wrapper<T>) -> Vec<T> {w.0} }這段代碼在舊版本中會編譯失敗,這是因為Rust的孤兒規則曾經更嚴格一些。要繞過這個,可以嘗試實現Into:
impl Into<TargetType> for OriginType {fn into(value: OriginType) -> TargetType {// 返回TargetType類型的值} } struct Wrapper<T>(Vec<T>);impl<T> Into<Vec<T>> for Wrapper<T> {fn into(self) -> Vec<T> {self.0} }重要的是要了解 Into 不提供 From 實現( From 卻提供 Into)。因此,應該始終嘗試先實現 From,如果 From 無法實現,則再嘗試使用 Into。
標準庫中的String類型就實現了Into<Vec<u8>>:is_hello的參數表示可以接收任意一個能夠轉換為Vec<u8>的類型。
fn is_hello<T: Into<Vec<u8>>>(s: T) {let bytes = b"hello".to_vec();assert_eq!(bytes, s.into()); }let s = "hello".to_string(); is_hello(s);注意事項
From和Into實現的轉換不允許出現錯誤,即轉換過程不能拋出異常。如果轉換過程允許拋出異常請使用:TryFrom和TryInto。
總結
以上是生活随笔為你收集整理的Rust学习之——From Trait和Into Trait以及“类型”到字符串的转换的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信开发 -- 二维码生成
- 下一篇: Second《C++ Primer》中文