[Rust] value와 ref는 비교할 수 없다

undefcat·2022년 9월 18일
0

rust

목록 보기
2/6
post-thumbnail

다음의 예제를 봅시다.

fn main() {
    let a = 10;
    let b_ref = &20;

    assert_eq!(a + b_ref, 30);

    assert!(a < b_ref);
}

ai32 타입이고, b_ref&i32 타입입니다. 과연 컴파일러는 뭐라고 할까요?

error[E0277]: can't compare `{integer}` with `&{integer}`
 --> src/main.rs:7:15
  |
7 |     assert!(a < b_ref);
  |               ^ no implementation for `{integer} < &{integer}` and `{integer} > &{integer}`
  |

a + b_ref 는 문제없지만, a < b_ref는 컴파일러가 씅냅니다. 사칙연산에 대해 valueref는 허용되지만, 비교연산자(< <= > >=)는 이를 허용하지 않는 것이죠.

Rust는 Trait로 연산자 오버로딩을 지원합니다. Rust 컴파일러는 연산자들을 특정 메서드 호출로 컴파일합니다.

즉, 사칙연산자들에 대해서는 refvalue간에 Trait가 구현되어 있다는 뜻이죠.

덧셈의 경우 std::ops::Add Trait의 구현이 필요합니다. Rust에서는 이를 지원하는데, 아래와 같이 value 타입과 ref 타입 모두를 구현해주고 있습니다. 물론 - * / % 모두 구현되어 있습니다.

반면, 비교의 경우 이를 지원하지 않습니다.

물론 평소에는 이게 큰 문제가 되진 않지만, 이 역시 함수형 프로그래밍 방식으로 사용할 땐 문제가 있을 수 있습니다. 예를 들어 어떤 배열에 30 이하의 값만 계산한다고 해봅시다.

fn main() {
    let numbers = vec![10, 20, 30, 40, 50];

    let result = numbers
        .iter()
        .filter(|n| n <= 30)
        .sum();

    assert_eq!(result, 60);
}

문제가 없을 것 같지만, 컴파일되지 않습니다.

error[E0308]: mismatched types
 --> src/main.rs:6:26
  |
6 |         .filter(|n| n <= 30)
  |                          ^^ expected `&&_`, found integer
  |

iter()는 reference를 리턴하고, filter() 역시 클로저의 매개변수를 reference로 넘깁니다. 따라서 filter()n의 타입은 &&i32 입니다. 이를 해결하려면 &&를 어떻게든 처리해야 합니다.

fn main() {
    let numbers = vec![10, 20, 30, 40, 50];

    let result = numbers
        .iter()
        .filter(|n| **n <= 30) // deref를 두 번 하거나
        .sum::<i32>();

    assert_eq!(result, 60);
}
fn main() {
    let numbers = vec![10, 20, 30, 40, 50];

    let result = numbers
        .iter()
        .filter(|&n| *n <= 30) // &와 패턴매칭하고 deref 하거나
        .sum::<i32>();

    assert_eq!(result, 60);
}
fn main() {
    let numbers = vec![10, 20, 30, 40, 50];

    let result = numbers
        .iter()
        .filter(|&&n| n <= 30) // i32는 Copy trait를 구현하므로 &&와 패턴매칭 하거나
        .sum::<i32>();

    assert_eq!(result, 60);
}
fn main() {
    let numbers = vec![10, 20, 30, 40, 50];

    let result = numbers
        .into_iter() // into_iter로 &나 *를 덜 쓰게 하거나. 하지만 이 경우, numbers의 ownership이 사라진다!
        .filter(|n| *n <= 30)
        .sum::<i32>();

    assert_eq!(result, 60);
   
}

방법은 여러가지가 있겠지만, 아무튼 중요한점은 비교의 경우 타입일치가 중요하다는 점을 기억해야 한다는 것입니다.

profile
undefined cat

0개의 댓글