AI/Rust

[Rust] usize, 익명 함수 클로저와 기본 메서드

mingchin 2024. 12. 3. 23:43
728x90
반응형

지난 글에서 구현했던 함수를 뜯어보자.

 

usize

Rust는 함수의 output type을 꼭 지정해주어야 한다. "unsigned size"의 준말로, 시스템 포인터 크기와 동일한 크기를 가지는 unsigned integer다. 요즘엔 거의 다 64bit 시스템을 사용하니 8바이트라 보면 되겠다.

 

hyp.chars().filter(|c| !c.is_whitespace()).collect();

굉장히 파이써닉한.. 표현이 놀랍다. 하나씩 뜯어보면 아래와 같다.

  1. hyp.chars():
    • hyp 문자열을 문자(char) 단위로 분리하여 이터레이터를 생성합니다.
    • 예: "hello world" -> ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
  2. .filter(|c| !c.is_whitespace()):
    • 이터레이터에서 각 문자를 검사하여 공백 문자가 아닌 문자만 남깁니다.
    • |c|는 클로저(익명 함수)로, 각 문자 c를 받아서 !c.is_whitespace() 조건을 검사합니다.
    • is_whitespace()는 문자가 공백인지 확인하는 메서드입니다.
    • 예: ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'] -> ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']
  3. .collect():
    • 필터링된 문자들을 모아서 새로운 컬렉션(예: 문자열)으로 만듭니다.
    • 기본적으로 collect()는 이터레이터의 요소들을 모아 벡터(Vec)로 반환하지만, 타입 힌트를 주면 문자열로도 변환할 수 있습니다.
let hyp = "hello world";
let result: String = hyp.chars().filter(|c| !c.is_whitespace()).collect();
println!("{}", result); // 출력: "helloworld"

예시 코드상에서, result 라는 변수에도 형을 지정하고 값을 할당하는 표현이 있다.

 

익명 함수, 클로저

파이썬의 lambda와 유사한 역할을 하는 것으로 보인다. " |c| = lambda c:" 와 동일한 표현이며, 변수는 아래 3가지 방법으로 할당(캡처)할 수 있다. 

  1. 참조로 캡처: &T - 클로저가 변수를 읽기 전용으로 캡처합니다.
  2. 가변 참조로 캡처: &mut T - 클로저가 변수를 수정할 수 있도록 캡처합니다.
  3. 값으로 캡처: T - 클로저가 변수를 소유하도록 캡처합니다.

기본적으로는 1의 참조로 캡처를 시도하며, 값으로 캡처를 하고 싶은 경우에는 "move"라는 별도 메서드를 사용해야 한다.

 

let haystack = vec![1, 2, 3];
let contains = move |needle| haystack.contains(needle);

println!("{}", contains(&1)); // true
println!("{}", contains(&4)); // false

// haystack을 다시 사용하려고 하면 컴파일 오류가 발생합니다.
// println!("There are {} elements in vec", haystack.len()); // 오류 발생

 

클로저가 변수의 소유권을 가져간다는 사실이 흥미롭다. 아마 사용과 동시에 변수를 해제하는? 느낌인 것 같다.

 

타입 캐스팅 as

"cer = (substitution_count + insertion_count + deletion_count) as f64" 와 같이 integer의 합산으로 계산한 결과를 f64 형으로 변환한다.

 

문자열의 할당 String::new()

빈 문자열을 할당하는 방법은 여러 가지가 있다.

# 1
let s = String::from("");

# 2
let s = "".to_string();

# 3
let s = String::with_capacity(0);

 

String::new()는 초기 버퍼를 할당하지 않는 mutable 객체로, 할당 시 메모리 비용이 저렴하다. 3의 방법과 동일하며, 0 대신 일정 바이트를 할당하는 경우 처음부터 그만큼의 메모리를 사용하게 되는데, 이는 추후 문자열을 추가할 때 재할당을 하지 않는 다는 점에서 차이가 있다.

 

io::stdout().flush().unwrap()

flush()는 버퍼에 쌓인 데이터를 즉시 출력하거나 전송한다고 한다.

use std::io::{self, Write};

fn main() {
    print!("Enter your name: ");
    // io::stdout().flush().unwrap(); // 이 줄을 주석 처리하면 메시지가 즉시 출력되지 않을 수 있습니다.
    let mut name = String::new();
    io::stdin().read_line(&mut name).unwrap();
    println!("Hello, {}!", name.trim());
}

 

실제 이렇게 실행해보니, 사용자 입력을 기다리는 동안 "Enter your name: "의 메시지가 출력되지 않는다.

 

unwrap()의 경우 해당 앞의 메서드가 정상적으로 실행되었는지 체크하는 예외처리로, 에러가 발생할 경우 프로그램을 패닉 상태로 만든다. (sys.exit()과 유사한 것 같다.)

io::stdout().flush().expect("Failed to flush stdout");

 

unwrap()이 아니라 expect()로 실행할 경우, 예외 발생에 대한 로깅을 할 수 있다.

728x90
반응형