From, TryFrom (Feat. Into, TryInto)

From 트레잇을 사용하면, 다른 타입을 대상 타입으로 변환할 수 있습니다.

이미 많이 사용해왔던 기능인데, String::from("foo") 등으로 사용해왔던 트레잇입니다:

#![allow(unused)]
fn main() {
use std::convert::*;

#[derive(Debug, PartialEq)]
struct Foo {
    bar: usize,
}

impl From<usize> for Foo {
    fn from(item: usize) -> Self {
        Foo { bar: item }
    }
}

let x = Foo::from(5);
assert_eq!(x, Foo { bar: 5 });
}

하지만 Box<T>같은 타입은 new 메서드를 사용해서 Box를 생성할 수 있습니다. (물론 From 트레잇도 구현되어 있으나, 둘 모두 같은 기능을 합니다.)

그럼 왜 굳이 From 트레잇을 구현하는가 하면, 매우 간단명료하게 대답할 수 있습니다:

Boxnewfrom은 제네릭 T를 받고, fromnew를 호출한합니다. (제네릭 T를 받지 않는 타입 (ex; String) 같은 경우, new 보단 from이 더욱 편합니다.)

그러므로 newfrom 메서드의 기능은 똑같지만, 후술할 Into 덕분에 from이 존재합니다.

Into

IntoFrom을 구현하면 자동으로 구현됩니다. 다만, 사용 방법이 조금 다릅니다:

#![allow(unused)]
fn main() {
use std::convert::*;

#[derive(Debug, PartialEq)]
struct Foo {
    bar: usize,
}

impl From<usize> for Foo {
    fn from(item: usize) -> Self {
        Foo { bar: item }
    }
}

let x = Foo::from(5);
assert_eq!(x, Foo { bar: 5 });

let x: Foo = 5usize.into();
assert_eq!(x, Foo { bar: 5 });
}

into를 사용할 땐, 컴파일러가 어떤 타입인지 모르기 때문에, 타입 어노테이션을 명시해주어야 합니다.

TryFrom, TryInto

관련 자료 등을 찾아보면 TryFromTryInto가 존재하는 것을 알 수 있는데, 이들은 이름 그대로 반환되는 값이 Result<T, E>의 경우일 때 사용합니다.

대표적으로, 타입 변환을 사용할 때 이용됩니다:

#![allow(unused)]
fn main() {
let x: Result<i64, _> = 5i32.try_into();
}

타입으로 쓰인 _는 반환 값을 무시하는 _ = expr; 문법이 아닌, infer 타입을 뜻합니다.

TryFrom 또한, From 트레잇과 비슷하게 구현할 수 있습니다:

#![allow(unused)]
fn main() {
use std::convert::*;

#[derive(Debug, PartialEq)]
struct Foo {
    bar: usize,
}

impl TryFrom<usize> for Foo {
    type Error = &'static str;

    fn try_from(value: usize) -> Result<Self, Self::Error> {
        if value != 0 {
            Ok(Foo { bar: value })
        } else {
            Err("Error")
        }
    }
}

let x = Foo::try_from(5);
assert_eq!(x, Ok(Foo { bar: 5 }));

let x = Foo::try_from(0);
assert_eq!(x, Err("Error"));

let _: Foo = 5usize.try_into().unwrap();
}

다만 차이점은, Error라는 연관 타입(associated type)이 존재하는데, 그냥 Result<T, E>의 제네릭 E 입니다.