이 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
을 사용하는 것이 좋다.
Borrow
AsRef
는 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" }
좋은 글 감사합니다.