hyunsu blog

홈강좌Rust 입문·중급 47편 중 23편째

Rust 메서드 — impl 블록에 구조체의 함수를 단다

목차 · 3
  1. 01impl 블록에 메서드를 만든다
  2. 02고치는 메서드는 &mut self
  3. 03실습 — self 없는 함수로 만들기

Rust 메서드는 impl 타입 { } 블록 안에 만드는, 구조체에 딸린 함수다. 첫 매개변수가 &self 면 읽기만 하고, &mut self 면 고친다. 고치는 메서드를 mut 아닌 변수에 부르면 E0596 이 난다. self 가 없는 함수는 Rect::new 처럼 부르고, 새 값을 만들 때 쓴다.

01 impl 블록에 메서드를 만든다

사각형 구조체에 넓이를 구하는 메서드를 달아 보자.

rust
struct Rect {
    width: u32,
    height: u32,
}

impl Rect {
    fn area(&self) -> u32 {
        self.width * self.height
    }

    fn is_square(&self) -> bool {
        self.width == self.height
    }
}

fn main() {
    let r = Rect { width: 30, height: 20 };
    println!("area: {}", r.area());
    println!("square: {}", r.is_square());
}
text
   Compiling rect v0.1.0 (~/projects/rect)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.16s
     Running `target/debug/rect`
area: 600
square: false
  • impl Rect { … } 는 "Rect 에 딸린 함수들" 을 담는 블록이다.
  • 메서드의 첫 매개변수 &self 는 "이 메서드를 부른 그 사각형" 을 빌린 것이다. &self 는 self: &Rect 를 줄여 쓴 것이라, 18편의 읽기 참조와 같다.
  • 안에서는 self.width 처럼 칸을 읽는다.
  • 부를 때는 r.area() 처럼 점 뒤에 쓴다. &self 자리에 r 이 저절로 들어간다. 9편의 scores.len() 이 이런 메서드였다.

함수 area(r: &Rect) 로 만들어도 같은 일을 한다. 메서드로 두면 "사각형이 할 수 있는 일" 이 impl Rect 한곳에 모여서 찾기 쉽다.

02 고치는 메서드는 &mut self

크기를 두 배로 키우는 메서드는 칸을 고친다. 그래서 &mut self 다. 그런데 mut 가 아닌 변수에 불러 보면 막힌다.

rust
struct Rect {
    width: u32,
    height: u32,
}

impl Rect {
    fn double(&mut self) {
        self.width = self.width * 2;
        self.height = self.height * 2;
    }
}

fn main() {
    let r = Rect { width: 30, height: 20 };
    r.double();
    println!("{} x {}", r.width, r.height);
}
text
error[E0596]: cannot borrow `r` as mutable, as it is not declared as mutable
  --> src/main.rs:15:5
   |
15 |     r.double();
   |     ^ cannot borrow as mutable
   |
help: consider changing this to be mutable
   |
14 |     let mut r = Rect { width: 30, height: 20 };
   |         +++
  1. 무엇 — r 을 고칠 수 있게(mutable) 빌릴 수 없다. mut 로 선언하지 않았기 때문이다.
  2. 어디 — 15번째 줄의 r.double(). double 이 &mut self 라서, 부르는 순간 r 을 &mut 로 빌린다.
  3. 고치는 법 — help: 가 14번째 줄을 let mut r 로 바꾸라고 한다.

19편의 규칙이 그대로다. 바꿀 수 없는 값을 고치라고 빌려줄 수는 없다.

첫 매개변수뜻부르는 변수
&self읽기만 한다let 이면 된다
&mut self칸을 고친다let mut 이어야 한다

03 실습 — self 없는 함수로 만들기

impl 안의 함수가 self 를 받지 않으면, 값 없이 타입 이름으로 부른다. 새 값을 만드는 함수가 대개 이렇다.

rust
struct Rect {
    width: u32,
    height: u32,
}

impl Rect {
    fn new(width: u32, height: u32) -> Self {
        Self { width, height }
    }

    fn square(size: u32) -> Self {
        Self { width: size, height: size }
    }

    fn double(&mut self) {
        self.width = self.width * 2;
        self.height = self.height * 2;
    }

    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let mut r = Rect::new(30, 20);
    r.double();
    let s = Rect::square(10);
    println!("{} x {} = {}", r.width, r.height, r.area());
    println!("square area: {}", s.area());
}
text
   Compiling rect v0.1.0 (~/projects/rect)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.15s
     Running `target/debug/rect`
60 x 40 = 2400
square area: 100
  • Rect::new(30, 20) 은 Rect 에 딸린 new 를 부른다. 5편의 i8::MAX 처럼 :: 는 "그 타입에 딸린 것" 이다. 15편부터 쓴 String::from 도 이런 함수다.
  • -> Self 의 Self 는 impl 하는 그 타입, 여기서는 Rect 를 가리킨다. 이름이 바뀌어도 impl 안은 고칠 필요가 없다.
  • Self { width, height } 는 22편에서 본 줄여 쓰기다.

new 라는 이름은 정해진 규칙이 아니라 관례다. 정사각형을 만드는 square 처럼 뜻이 드러나는 이름을 더 만들어도 된다. Rect 에 둘레를 구하는 perimeter(&self) -> u32 를 더해 보자. 다음 편은 "정해진 몇 가지 중 하나" 를 나타내는 열거형이다.