Rust 기초: From과 Into 트레잇
자료형 간의 명시적이고 안전한 데이터 변환은 Rust의 중요한 철학 중 하나입니다.
Rust는 From
과 Into
라는 표준 트레잇을 제공하여 데이터 변환을 안전하고 명확하게 할 수 있도록 돕는데요.
이 글에서는 이 두 트레잇의 관계와 차이점, 그리고 활용법을 살펴보겠습니다.
From 트레잇이란?
From
트레잇은 다른 자료형부터(from) 현재 자료형으로 변환하는 방법을 정의할 때 사용합니다.
From
트레잇의 from()
메서드는 다른 제네릭(generic) 타입을 인자로 받고 자신의 타입을 반환합니다.
pub trait From<T> {
fn from(T) -> Self;
}
예를 들어, Rust에 내장된 String
자료형에는 &str
자료형으로 부터 변환되는 방법이 정의되어 있습니다.
impl From<&str> for String {
fn from(s: &str) -> Self {
String::new() + s
}
}
String::from()
메서드를 덕분에 우리는 &str
자료형을 String
자료형으로 편하게 바꿀 수 있는 것이지요.
let str = String::from("Rust");
Into 트레잇이란?
Into
트레잇은 From
트레잇과 정반대의 반대 관점에서 데이터 변환 방법을 정의합니다.
즉, 현재 자료형이 다른 자료형으로(into)로 변환되는 방법을 정의할 수 있습니다.
Into
트레잇의 into()
메서드는 자신의 타입을 인자로 받고 다른 제네릭(generic) 타입을 반환합니다,
pub trait Into<T> {
fn into(self) -> T;
}
From
트레잇을 구현하면 컴파일러가 Into
트레잇은 자동으로 구현해줍니다.
따라서 Into
트레잇을 직접 구현한 일은 거의 없습니다.
예를 들어, &str
자료형을 String
자료형으로 바꿀 때, into()
메서드를 사용할 수도 있습니다.
let str: String = "Rust".into();
From 트레잇 구현
구조체나 열거형을 상대로 From 트레잇을 구현해보겠습니다.
예를 들어, x
와 y
필드로 이루어진 Point
구조체를 정의하고, 튜플로부터 반환하는 방법을 From
트레잇으로 구현해보겠습니다.
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
impl From<(i32, i32)> for Point {
fn from(coords: (i32, i32)) -> Self {
let (x, y) = coords;
Self { x, y }
}
}
Point::from()
메서드로도 변환할 수 있고, 튜플.into()
메서드로도 변환할 수 있게 됩니다.
fn main() {
let point1 = Point::from((1, 2));
println!("{point1:?}");
let point2: Point = (3, 4).into();
println!("{point2:?}");
}
Point { x: 1, y: 2 }
Point { x: 3, y: 4 }
TryFrom과 TryInto 트레잇
From
과 Into
트레잇은 데이터 변환이 항상 성공하는 상황에서 사용합니다.
데이터 변환이 실패할 가능성이 있다면 TryFrom
과 TryInto
트레잇을 사용해야합니다.
표준 라이브러리에 두 트레잇은 다음과 같이 정의되어 있습니다.
From
과 Into
트레잇과 다르게 반환 자료형이 Result
임을 알 수 있습니다.
이를 통해서 데이터 변환이 실패햇을 때 발생할 수 있는 오류를 명시해줄 수 있습니다.
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>;
}
TryFrom 구현
TryInto
트레잇도 TryFrom
트레잇만 구현하면 컴파일러가 자동으로 구현해줍니다.
이번엔느 필드 값으로 양의 정수만 허용하는 PositivePoint
구조체를 정의하고,
TryInto
트레잇을 구현하여 음수가 포함된 튜플이 인자로 들어오면 오류를 발생시키도록 구현해보겠습니다.
#[derive(Debug)]
struct PositivePoint {
x: u32,
y: u32,
}
impl TryFrom<(i32, i32)> for PositivePoint {
type Error = String;
fn try_from(coords: (i32, i32)) -> Result<Self, Self::Error> {
let (x, y) = coords;
if x < 0 || y < 0 {
Err(String::from("Negative coordinate"))
} else {
Ok(Self {
x: x as u32,
y: y as u32,
})
}
}
}
마찬가지로 try_from()
메서드와 try_into()
메서드, 모두 사용해서 데이터 변환을 할 수 있습니다.
fn main() {
let ok = PositivePoint::try_from((10, 20));
let err: Result<PositivePoint, String> = (10, -5).try_into();
println!("{:?}", ok);
println!("{:?}", err);
}
Ok(PositivePoint { x: 10, y: 20 })
Err("Negative coordinate")
FromStr
데이터 변환 중에서 특히 문자열로부터 어떤 자료형으로 변화하는 작업은 매우 빈번하죠?
그래서 Rust의 표준 라이브러리에서는 From
트레잇의 특별한 형태인 FromStr
도 제공합니다.
FromStr
트레잇은 다음과 같이 정의되어 있습니다.
TryFrom
과 마찬가지로 Result
를 반환하여 발생할 가능성이 있는 오류를 표시해주도록 되어 있습니다.
pub trait FromStr: Sized {
type Err;
fn from_str(s: &str) -> Result<Self, Self::Err>;
}
FromStr
트레잇의 from_str()
메서드를 구현하면 컴파일러가 자동으로 그 반대 작업을 해주는 parse()
메서드도 구현을 해줍니다.
use std::str::FromStr;
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
impl FromStr for Point {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.trim().split(',').collect();
if parts.len() != 2 {
return Err(String::from("Invalid format"));
}
let x = parts[0].parse::<i32>().map_err(|e| e.to_string())?;
let y = parts[1].parse::<i32>().map_err(|e| e.to_string())?;
Ok(Point { x, y })
}
}
FromStr
트레잇은 Rust의 많은 내장 자료형에 대해서 이미 구현이 되어 있습니다.
위 코드에서 i32
타입인 parts[0]
과 parts[1]
상대로 parse()
메서드를 호출할 수 있는 이유입니다.
fn main() {
let good = Point::from_str("10,20");
let bad1: Result<Point, String> = "abc,xyz".parse();
let bad2: Result<Point, String> = "42".parse();
println!("{:?}", good);
println!("{:?}", bad1);
println!("{:?}", bad2);
}
Ok(Point { x: 10, y: 20 })
Err("invalid digit found in string")
Err("Invalid format")
마치며
지금까지 Rust에서 데이터 변환의 핵심 매커니즘인 From
과 Into
트레잇에 대해서 알아보았습니다.
또한 같이 알아두면 좋은 TryFrom
, TryInto
, FromStr
트레잇에 대해서도 살펴보았습니다.
데이터 변환을 하실 때 본 포스팅에서 다룬 트레잇을 적지적소에 잘 활용하실 수 있으셨으면 좋겠습니다.