본문 바로가기

Rust

[Rust] 함수 값 전달(Passing Arguments by Value) 완벽 가이드

반응형

Rust의 값 전달(Passing Arguments by Value) 완벽 가이드

소개

Rust에서 함수에 인자를 전달하는 방식 중 가장 기본이 되는 '값 전달(Pass by Value)'에 대해 자세히 알아보겠습니다. Rust의 독특한 소유권 시스템과 함께 이해하면 더욱 효과적인 코드를 작성할 수 있습니다.

Rust Function

기본 개념

Rust에서 값을 전달할 때는 기본적으로 소유권이 이전됩니다. 이것이 Rust의 가장 큰 특징 중 하나입니다.

fn main() {
    let name = String::from("Alice");
    print_name(name);
    // println!("{}", name); // 🚫 컴파일 에러! name의 소유권이 이전됨
}

fn print_name(n: String) {
    println!("Name: {}", n);
}

기본 타입(Primitive Types)의 값 전달

기본 타입은 Copy 트레이트를 구현하고 있어 값이 복사됩니다.

fn main() {
    let number = 42;
    multiply_by_two(number);
    println!("Original number: {}", number); // 여전히 42 사용 가능!
}

fn multiply_by_two(n: i32) -> i32 {
    n * 2
}

구조체의 값 전달

#[derive(Debug)]
struct Person {
    name: String,
    age: u32,
}

fn main() {
    let person = Person {
        name: String::from("Bob"),
        age: 30,
    };
    
    print_person(person);
    // println!("{:?}", person); // 🚫 컴파일 에러! person의 소유권이 이전됨
}

fn print_person(p: Person) {
    println!("Person: {:?}", p);
}

Copy 트레이트 구현하기

구조체에 Copy 트레이트를 구현하여 값 복사가 가능하게 만들 수 있습니다.

#[derive(Debug, Copy, Clone)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let point = Point { x: 10, y: 20 };
    print_point(point);
    println!("Original point: {:?}", point); // 여전히 사용 가능!
}

fn print_point(p: Point) {
    println!("Point: {:?}", p);
}

튜플과 배열의 값 전달

튜플과 배열도 기본 타입처럼 처리될 수 있습니다.

fn main() {
    // 튜플 예제
    let coords = (10, 20);
    process_coordinates(coords);
    println!("Original coords: {:?}", coords); // Copy 트레이트 구현으로 사용 가능

    // 배열 예제
    let numbers = [1, 2, 3, 4, 5];
    sum_array(numbers);
    println!("Original array: {:?}", numbers); // 여전히 사용 가능
}

fn process_coordinates(coords: (i32, i32)) {
    println!("Processing coordinates: {:?}", coords);
}

fn sum_array(arr: [i32; 5]) -> i32 {
    arr.iter().sum()
}

값 전달과 소유권 이전을 함께 사용하기

실제 애플리케이션에서는 값 전달과 소유권 이전을 적절히 혼합하여 사용합니다.

#[derive(Debug)]
struct User {
    name: String,
    settings: UserSettings,
}

#[derive(Debug, Copy, Clone)]
struct UserSettings {
    dark_mode: bool,
    notifications: bool,
}

fn main() {
    let user = User {
        name: String::from("Charlie"),
        settings: UserSettings {
            dark_mode: true,
            notifications: false,
        },
    };

    // settings는 Copy 가능하므로 복사됨
    process_settings(user.settings);
    
    // name은 String이므로 소유권 이전
    process_user(user);
    // println!("{:?}", user); // 🚫 컴파일 에러!
}

fn process_settings(settings: UserSettings) {
    println!("Processing settings: {:?}", settings);
}

fn process_user(user: User) {
    println!("Processing user: {:?}", user);
}

값 반환하기

 

함수에서 값을 반환할 때도 소유권이 이전됩니다.

fn main() {
    let name = create_user_name();
    println!("Created name: {}", name);
    // name의 소유권을 가짐
}

fn create_user_name() -> String {
    String::from("David")
}

성능 고려사항

값 전달 시 크기가 큰 데이터의 경우 성능에 영향을 줄 수 있습니다.

#[derive(Debug)]
struct LargeData {
    buffer: [u8; 1000000], // 1MB 크기의 배열
}

fn main() {
    let data = LargeData {
        buffer: [0; 1000000],
    };
    
    // 큰 데이터를 값으로 전달하면 비효율적일 수 있음
    process_large_data(data);
}

fn process_large_data(data: LargeData) {
    println!("Processing large data...");
}

실전 팁과 베스트 프랙티스

  1. 작은 크기의 기본 타입은 값으로 전달
  2. 큰 구조체나 문자열은 참조로 전달
  3. Copy 트레이트 구현은 작은 구조체에만 사용
  4. 소유권 이전이 필요한 경우 명시적으로 값 전달 사용
fn main() {
    // 기본 타입: 값으로 전달
    let number = 42;
    process_number(number);

    // 큰 데이터: 참조로 전달
    let text = String::from("Hello, World!");
    process_text(&text);
    println!("Can still use text: {}", text);
}

fn process_number(n: i32) {
    println!("Number: {}", n);
}

fn process_text(t: &String) {
    println!("Text: {}", t);
}

결론

Rust의 값 전달 시스템은 소유권 개념과 밀접하게 연관되어 있습니다. 기본 타입의 복사, 구조체의 소유권 이전, Copy 트레이트 구현 등을 적절히 활용하면 안전하고 효율적인 코드를 작성할 수 있습니다. 특히 성능과 메모리 사용을 고려할 때 값 전달 방식을 신중히 선택하는 것이 중요합니다.

 

 

반응형