45、TryFrom Trait

TryFromTryInto

 

上一个章节我们学到了From trait和Into trait,这是Rust的用于无误类型转换的惯用接口。

但是,如果不能保证转换一定成功该怎么办呢?

 

现在我们对错误有了足够的了解,可以讨论FromInto能够进行错误处理的版本:TryFromTryInto

 

TryFromTryInto

 

如同FromIntoTryFromTryInto都在std::convert中定义。

pub trait TryFrom<T>: Sized {
    type Error;
    fn try_from(value: T) -> Result<Self, Self::Error>;
}

pub trait TryInto<T>: Sized {
    type Error;
    fn try_into(self) -> Result<T, Self::Error>;
}

 

From/Into and TryFrom/TryInto 的主要区别在于后者返回的是Result类型。

这样就允许转换失败,返回一个错误而不是panic。

 

Self::Error

 

TryFromTryInto类型都有关联的Error类型。这使得每种实现可以指定自己的错误类型,理想情况下应为所执行转换最合适的错误类型。

 

Self::Error是一种引用trait本身定义的Error关联类型的方法。

 

Duality(对偶性)

 

就像FromInto一样,TryFromTryInto也是dual traits。如果我们为某个类型实现了TryFrom,我们也可以直接获得TryInto

 

练习

 

累了,这次的联系直接看答案吧还是(

 

原题:

// TODO: Implement `TryFrom<String>` and `TryFrom<&str>` for `Status`.
//  The parsing should be case-insensitive.

#[derive(Debug, PartialEq, Clone)]
pub enum Status {
    ToDo,
    InProgress,
    Done,
}

/* TODO */

/* TODO */

/* TODO */

 

观察题目,题目想让我们定义一个错误枚举类型,然后实现TryFromTryInto这两个trait,并且能够正确处理错误。

 

我们看答案:

// TODO: Implement `TryFrom<String>` and `TryFrom<&str>` for `Status`.
//  The parsing should be case-insensitive.

#[derive(Debug, PartialEq, Clone)]
pub enum Status {
    ToDo,
    InProgress,
    Done,
}

#[derive(Debug, thiserror::Error)]
#[error("{invalid_status} is not a valid status")]
pub struct ParseStatusError {
    invalid_status: String,
}

impl TryFrom<String> for Status {
    type Error = ParseStatusError;

    fn try_from(value: String) -> Result<Self, Self::Error> {
        value.as_str().try_into()
    }
}

impl TryFrom<&str> for Status {
    type Error = ParseStatusError;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        match value.to_lowercase().as_str() {
            "todo" => Ok(Status::ToDo),
            "inprogress" => Ok(Status::InProgress),
            "done" => Ok(Status::Done),
            _ => Err(ParseStatusError {
                invalid_status: value.to_string(),
            }),
        }
    }
}

 

这段代码是定义错误枚举类型

#[derive(Debug, thiserror::Error)]
#[error("{invalid_status} is not a valid status")]
pub struct ParseStatusError {
    invalid_status: String,
}

实现了Debugthiserror::Error这两个派生宏。同时设定了error的错误信息,囊括了结构体里面的invalid_status成员。

 

这段代码是在实现<Status>.from(<String>),定义了一个关联类型Error,关联类型就是刚刚我们自定义的错误枚举类型,然后实现try_from方法,参数是String类型的一个变量,返回值是Result类型,这里直接调用了&str->Status的Into方法,这里体现了对偶性。

impl TryFrom<String> for Status {
    type Error = ParseStatusError;

    fn try_from(value: String) -> Result<Self, Self::Error> {
        value.as_str().try_into()
    }
}

 

这段代码实现了真正的转换流程:

impl TryFrom<&str> for Status {
    type Error = ParseStatusError;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        match value.to_lowercase().as_str() {
            "todo" => Ok(Status::ToDo),
            "inprogress" => Ok(Status::InProgress),
            "done" => Ok(Status::Done),
            _ => Err(ParseStatusError {
                invalid_status: value.to_string(),
            }),
        }
    }
}

 

唯一不同的地方是,方法体的内部实现不一样,因为题目说是转换都是小写的。于是match语句的后面先调用了to_lowercase()再调用as_str(),转换为了str类型。

然后模式匹配,首先匹配三个Ok(),直接返回Status对应的枚举变体。

最后一个通配符返回错误处理,这个错误结构体携带了错误的值,作为一个Self::Error返回了,这是一个结构体,一个被Err()包裹的错误类状态结构体。

 

不过这个Self::Error是怎么和ParseStatusError绑定的呢?

难道说,Self在这里不是Status?(其实就是Status…)

 

这一节的内容就先到这吧。

阅读剩余
THE END