100 Exercises To Learn Rust - 4.10 assoc vs generic 풀기

연관 타입(associated type)

type Target;과 같이 트레이트에서 자리표시자 타입을 명시하면, 트레이트 구현자는 구체적인 타입을 지정할 수 있다.

 

트레이트가 구현될 때까지 어떤 타입인지 정확히 알지 못해도 트레이트를 정의할 수 있다.

 

연관 타입을 사용하는 Deref가 구현된 Stringstr로만 변환 가능하다. 다른 타입으로도 변환이 된다면 혼란스러우므로, 자리표시자(Target 등)으로 명시된 하나의 타입만 변환할 수 있다.

제네릭(Generic trait)

제네릭과 비교하면 차이가 드러난다.

 

제네릭을 사용하는 From은 어떤 타입에도 구현할 수 있다. 연관 타입과 달리 컴파일러는 변환되는 값의 유형에 따라 어떤 구현을 사용할지 결정할 수 있으므로 모호함(Ambiguity)이 없다.

풀이

// TODO: self의 n제곱을 반환하는 power 메서드를 가진 Power 트레이트를 정의하세요.
// 트레이트 정의와 구현은 테스트를 컴파일하고 통과할 수 있어야 합니다.

// 추천: 모든 케이스를 다루려고 제네릭 구현을 시도하겠지만, 이는 꽤 복잡하고,
// num-traits와 같은 크레이트를 필요로 합니다.
// 그러니 고도의 제네릭 구현을 피하려면 간단한 매크로르 사용하는 편이 좋습니다.
// "Little book of Rust macros"(https://veykril.github.io/tlborm/)를 참고하세요.
// 하지만 꼭 그래야 할 필요는 없습니다. 세 개의 분리된 구현을 작성해도 괜찮습니다.
// 당신이 궁금하다면 더 많은 걸 탐구해보세요.

pub trait Power<RHS = Self> {
    type Output;
    fn power(self, rhs:RHS) -> Self::Output;
}

impl Power<u16> for u32 {
    type Output = u32;
    fn power(self, rhs:u16) -> Self::Output {
        let mut res = 1;
        for _ in 0..rhs {
            res *= self;
        }
        res
    }
}

impl Power<u32> for u32 {
    type Output = u32;
    fn power(self, rhs:u32) -> Self::Output {
        let mut res = 1;
        for _ in 0..rhs {
            res *= self;
        }
        res
    }
}

impl Power<&u32> for u32 {
    type Output = u32;
    fn power(self, rhs:&u32) -> Self::Output {
        let mut res = 1;
        for _ in 0..*rhs {
            res *= self;
        }
        res
    }
}

#[cfg(test)]
mod tests {
    use super::Power;

    #[test]
    fn test_power_u16() {
        let x: u32 = 2_u32.power(3u16);
        assert_eq!(x, 8);
    }

    #[test]
    fn test_power_u32() {
        let x: u32 = 2_u32.power(3u32);
        assert_eq!(x, 8);
    }

    #[test]
    fn test_power_ref_u32() {
        let x: u32 = 2_u32.power(&3u32);
        assert_eq!(x, 8);
    }
}

 

u32 타입에 대하여 매개변수의 타입만 달라지고 있기 때문에 제네릭 매개변수와 타입 자리표시자를 함께 사용했다.

 

각각의 테스트에 대해 별도로 동작하는 세 개의 메서드를 구현했다.

 

리턴 타입은 u32로 모두 동일하고, rhs만큼 제곱하여 반환하도록 한다.

 

제곱은 pow()가 아니라 직접 구현했다.

 

&u32에 대해서는 역참조(*)하여 값을 가져와 그만큼 제곱한다.