이 module 내부에 있는 trait은 다른 타입을 변환하는 module이고, 각 trait은 각각의 목적을 제공한다.
AsRef은 cheap reference-to-reference conversion을 제공한다.AsMut은 cheap mutable-to-mutable conversion을 제공한다.From은 value를 consuming하는 value-to-value conversion을 제공한다.Into는 value를 consuming하고, 현재 crate 외부에 있는 type value-to-value을 conversion을 제공한다.TryFrom 과 TryInto trait은 From과 Into와 같이 작동하지만, conversion이 fail할 수도 있을 때 써야 한다. trait은 다양한 type의 arguments와 같은 경우를 위한 generic 함수에도 활용된다.From과 TryFrom이 더 유연하고 standard library의 blanket implementation(포괄적 구현) 덕에 Into나 TryInto의 기능도 제공하기 때문에, Into<U>와 TryInto<U> 보다는 From<T>와 TryFrom<T>를 사용할 것을 권장한다.
pub trait AsRef<T>
where
T: ?Sized,
{
// Required method
fn as_ref(&self) -> &T;
}
cheap reference-to-reference conversion을 제공함. mutable reference 사이의 conversion을 제공하는AsMut와 유사하다. 보다 costly(비싼) conversion을 하기 위해선 &T와 From을 사용하는 것이 좋다.
BorrowAsRef는 Borrow와 유사하지만, 몇가지 다른 점이 있다.
AsRef와 달리 Borrow는 모든 T를 위한 blanket impl(포괄적인 구현)을 갖고 있고, reference와 value를 받을 수 있다.Borrow는 borrowed value의 Hash, Eq 그리고 Ord가 owned value의 그것과 동일해야 한다. 따라서, struct의 single field를 빌리길 원한다면, AsRef로 구현할 수 있다. struct의 value에 다한 참조를 immutable 참조로 변환한다. 단 주의할 점은 모든 field의 type에 따라 선언해줘야 한다는 점이다. 아래 예시에선 i32 타입과 String타입에 맞춰서 구현했다.
use std::convert::AsRef;
#[derive(Debug)]
struct number {
i32: i32,
string: String
}
impl AsRef<i32> for number{
fn as_ref(&self) -> &i32 {
&self.i32
}
}
impl AsRef<String> for number{
fn as_ref(&self) -> &String {
&self.string
}
}
fn main(){
let c = number{
i32: 1,
string: "value: 1".to_string(),
};
let n: &i32 = c.as_ref();
println!("{}", n);
let p: &String = c.as_ref();
println!("{}", p);
}
pub trait AsMut<T>
where
T: ?Sized,
{
// Required method
fn as_mut(&mut self) -> &mut T;
}
cheap mutable-to-mutable reference conversion을 제공한다. AsRef와 유사하지만, mutable reference끼리 변환할 때 사용한다. 이 trait은 절대 fail해선 안된다. 만약 conversion이 fail할 수도 있는 경우, Opion<T>나 Result<T>를 반환하는 dedicated method를 사용하자.
모든 mutably dereferenceable type에 해당되는 말은 아니지만, AsMut는 inner type이 mutable reference라면 auto-deference를 한다. (e.g: 만약 foo가 &mut Foo혹은 &mut &mut Foo type을 갖고 있다면 foo.as_mut()는 동일하게 작동한다. foo.as_mut()은 BOX::new(foo)as.mut()와 동일하게 작동하지 않는다. 많은 smart pointer는 cheap reference-to-reference 변환을 하진 않지만, pointed-to value를 반환하며 as_mut의 기능을 제공한다. 하지만, AsMut::as_mut은 mutable dereferencing의 목적으로만 사용하지 말아야 한다.
let mut x = Box::new(5i32);
// Avoid this:
// let y: &mut i32 = x.as_mut();
// Better just write:
let y: &mut i32 = &mut x;
mutable 참조 (&mut T)와 AsMut trait을 이용해 참조를 mutable 참조로 바꾸는 둘의 차이점은 참조되는 값의 type이다. AsMut trait을 사용하면 모든 유형의 값에 대한 참조를 동일한 값에 대한 변경 가능한 참조로 변환할 수 있다.
use std::convert::AsRef;
use std::fmt::Debug;
#[derive(Debug)]
struct number {
i32: i32,
string: String,
}
impl AsMut<i32> for number {
fn as_mut(&mut self) -> &mut i32 {
&mut self.i32
}
}
fn modify_asmut<T: AsMut<i32> + ?Sized> (v: &mut T, value: i32){
*v.as_mut() = value;
}
fn main() {
let mut c = number {
i32: 1,
string: "value: 1".to_string(),
};
let w = &mut c;
w.i32 = 3;
println!("{:?}", w); // 3
modify_asmut(w, 4);
println!("{:?}", w);
}
Into의 reciprocal(역수)다. From trait은 type별로 생성자를 overloading할 수 있게 해준다. 물론 rust에서 생성자(constructor) 개념은 존재하지 하지만 않지만 이해를 위해서 생성자라는 단어를 사용했다.
use std::fmt::Debug;
#[derive(Debug)]
struct number {
i32: i32,
string: String,
}
impl From<i32> for number {
fn from(item: i32) -> number{
number {
i32: item,
string: item.to_string()
}
}
}
fn main() {
let t: number = From::from(4);
println!("{:?}", t);
} // number { i32: 4, string: "4" }
From의 reciprocal(역수)다. From trait을 구현했다면 따로 함수를 추가하지 않아도 된다.
use core::num;
use std::fmt::Debug;
#[derive(Debug)]
struct number {
i32: i32,
string: String,
}
impl From<i32> for number {
fn from(item: i32) -> number{
number {
i32: item,
string: item.to_string()
}
}
}
fn main() {
let t: number = 4.into();
println!("{:?}", t);
} // number { i32: 4, string: "4" }
좋은 글 감사합니다.