# Rust Basic[변수~라이프타임 입문하기]

# 일반 프로그래밍

## 변수

러스트에서 변수는 기본적으로 불변이다. 따라서 `let x = 5;`와 같이 선언하면 이는 기본적으로 불변이다. 따라서 이 값을 바꾸고자 한다면 컴파일이 불변성 에러를 뱉을 것이다.

따라서 변수를 가변으로 만들고자 한다면 `let mut x = 5;`처럼 반드시 `mut` 키워드를 붙여야 한다.

## 상수

사실 불변 변수라면 그냥 상수와 구분되지 않아도 되는 것이 아닌가 싶지만 상수는 `let` 대신 `const` 키워드로 선언하고 `mut`와 함께 사용할 수 없다.

그리고 기본적으로 불변 변수는 런타임에 바인딩되지만 상수는 컴파일 타임에 인라인된다. 불변 변수와 상수는 비슷한 것이라기보단 변수에 가변 옵션을 꺼놓은 것과 비슷한다. 유효한 범위도 불변 변수와 상수는 다르다.

그리고 변수는 타입 생략이 가능하지만 상수는 언제나 선언해주어야 한다.

```rust
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3
```

+) `섀도잉` 러스트에서 같은 이름으로 변수를 다시 선언할 수 있으나 이건 `mut` 키워드로 가변 변수의 값을 바꿔주는 것과 다르다.

같은 이름으로 선언하는 것은 변수를 다시 만드는 것이고 값을 바꿔주는 것과는 다르다.

따라서 기본적으로 타입 간 변경이 불가능한 가변 변수에서

```rust
let mut spaces = " ";
spaces = spaces.len();
```

로 서로 다른 타입으로 값을 바꾸고자 하면 컴파일 에러가 일어나지만

```rust
let spaces = " ";
let spaces = spaces.len();
```

는 에러를 일으키지 않는다는 것이다.

## 함수

러스트에서 함수는 `fn` 키워드로 생성하고 `->` 뒤에 반환값의 타입을 선언한다. 그리고 세미콜론 없이 값을 적어줘서 반환값을 지정할 수 있는데

```rust
fn five() -> i32 {
	5
}
```

이러한 식이다. 반환값의 타입이 일치하지 않으면 이 또한 컴파일 에러를 일으킨다.

## 조건문

딱히 특별할 건 없고

```rust
fn main() {
    let number = 6;

    if number % 4 == 0 {
        println!("number is divisible by 4");
    } else if number % 3 == 0 {
        println!("number is divisible by 3");
    } else if number % 2 == 0 {
        println!("number is divisible by 2");
    } else {
        println!("number is not divisible by 4, 3, or 2");
    }
}
```

이런 식으로 쓰면 되겠다.

## 반복문

`loop` 키워드는 코드를 명시적으로 그만두라고 하기 전까지 반복문이 계속 돌아간다. 이걸 어디 쓰냐 하면 어떤 스레드가 실행 완료되었는지 검사하는 데에 쓸 수 있다. 대체로 실패할지도 모르는 연산을 재시도하는 경우로 보면 되겠다. `break` 이후에 값을 반환할 수도 있는데 그것은

```rust
fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };

    println!("The result is {result}");
}
```

이런 식으로 써주면 되겠다.

그리고 라벨을 지정해서 특정한 루프에 적용되도록 할 수 있는데

```rust
fn main() {
    let mut count = 0;
    'counting_up: loop {
        println!("count = {count}");
        let mut remaining = 10;

        loop {
            println!("remaining = {remaining}");
            if remaining == 9 {
                break;
            }
            if count == 2 {
                break 'counting_up;
            }
            remaining -= 1;
        }

        count += 1;
    }
    println!("End count = {count}");
}
```

이런 식으로.

`while`이나 `for` 문도 다른 프로그래밍 언어랑 크게 다를 것은 없다.

```rust
fn main() {
    let a = [10, 20, 30, 40, 50];
    let mut index = 0;

    while index < 5 {
        println!("the value is: {}", a[index]);

        index += 1;
    }
}
```

```rust
fn main() {
    let a = [10, 20, 30, 40, 50];

    for element in a {
        println!("the value is: {element}");
    }
}
```

이런 식으로도 쓸 수 있다.

```rust
fn main() {
    for number in (1..4).rev() {
        println!("{number}!");
    }
    println!("LIFTOFF!!!");
}
```

---

# 구조체 & 열거형

## 구조체

### 구조체 정의하기

러스트에서 구조체도 C에서와 크게 다르지 않다.

```rust
struct User {
	active: bool,
	username: String,
	email: String,
	sign_in_count: u64,
}
```

이렇게 써주면 되겠다.

사용도

```rust
fn main() {
	let user1 = User {
		active: true,
		username: String::from("username123"),
		email: String::from("email@email.com"),
		sign_int_count: 1,
	};
}
```

이런 식으로 써주면 되겠다.

똑같이 `mut` 키워드로 가변성을 지정해줄 수 있는데

```rust
fn main() {
	let mut user1 = User {
		active: true,
		username: String::from("username");
		email: String::from("email.com");
		sign_int_count: 1,
	};
	user1.email = String::from("other.com");
}
```

가변성은 인스턴스 전체가 지니게 되어서 모든 필드가 가변적이게 된다. 특정 필드만 가변 나머지는 불변 이렇게 만드는 것은 불가능하다.

다음 코드를 보자.

```rust
fn build_user(email: String, username: String) -> User {
    User {
        active: true,
        username: username,
        email: email,
        sign_in_count: 1,
    }
}
```

여기서 변수명과 필드명이 같아서 `username: username`과 같이 써주었는데 이걸 생략할 수 있다. 즉

```rust
fn build_user(email: String, username: String) -> User {
    User {
        active: true,
        username,
        email,
        sign_in_count: 1,
    }
}
```

이런 식으로 쓸 수 있다.

### 구조체 업데이트 문법

또 기존 인스턴스를 이용해 새 인스턴스를 만들 때 축약된 구조체 업데이트 문법을 사용할 수 있는데

```rust
fn main() {
    // --생략--

    let user2 = User {
        active: user1.active,
        username: user1.username,
        email: String::from("another@example.com"),
        sign_in_count: user1.sign_in_count,
    };
}
```

가 있다고 할 때 이것을

```rust
fn main() {
    // --생략--

    let user2 = User {
        email: String::from("anotheremail@example.com"),
        ..user1
    };
}
```

이런 식으로 쓸 수 있다.

### 튜플 구조체

러스트는 튜플같은 구조체도 지원하는데 이를 `튜플 구조체`라고 부른다. 튜플 구조체는 이름은 있지만 필드가 없고, 튜플처럼 인덱스로 접근하는 구조체이다.

```rust
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

fn main() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);

    println!("Black: ({}, {}, {})", black.0, black.1, black.2);
    println!("Origin: ({}, {}, {})", origin.0, origin.1, origin.2);
}
```

이런 식으로 쓸 수 있다.

### 유닛 구조체

필드가 없는 구조체도 있는데 이를 `유닛 구조체`라고 부른다.

```rust
struct AlwaysEqual; // 필드가 없다!
fn main() {
    let subject = AlwaysEqual; 
    let subject2 = AlwaysEqual;

    if subject == subject2 {
        println!("They are equal!");
    }
}
```

### 구조체에 디버깅 출력

러스트는 구조체에 디버깅 출력을 할 수 있는 `Debug` 트레이트를 제공한다. 그런데 구조체에 해당 기능을 적용하려면 명시적인 동의가 필요하다. `#[derive(Debug)]`를 구조체 정의 위에 붙여주면 된다.

그러면

```rust
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect = Rectangle {
        width: 30,
        height: 50,
    };
    
    println!("rect is {:#?}", rect); // {:#?}는 pretty print
    println!("rect is {:?}", rect); // {:?}는 일반 print
 }
```

이런 식으로 구조체를 출력할 수 있다.

## 메서드

메서드는 구조체에 정의된 함수로, 구조체의 인스턴스에 대해 호출할 수 있다.

```rust
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

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

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!("The area of the rectangle is {} square pixels.", rect1.area());
    println!("rect1 is {:#?}", rect1);
}
    }
}
```

로 쓸 수 있는데

`Rectangle` 의 컨택스트에 `impl` 블록을 사용하여 메서드를 정의한다.

### 연관 함수

`impl` 블록 내에 구현된 모든 함수를 `연관 함수`라고 부른다. 이건 `impl` 뒤에 나오는 타입과 모두 연관되어 있기 때문에 이런 이름이 붙었다.

메서드가 아닌 연관 함수도 존재하는데 이건 구조체의 새 인스턴스를 반환하는 생성자로 자주 활용된다.

### 여러개의 `impl` 블록

각 구조체는 여러 개의 `impl` 블록을 가질 수 있다. 예컨대

```rust
impl Rectangle {
    fn new(width: u32, height: u32) -> Rectangle {
        Rectangle { width, height }
    }
}

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

이런 식으로 여러 개의 `impl` 블록을 정의할 수 있다. <s>다만 여기서 이렇게 쓰는 게 유리하지는 않다.</s>

## 열거형

열거형은 어떤 값이 여러 개의 가능한 값의 집합 중 하나라는 것을 나타내는 방법을 제공한다. 열거형으로 IPv4와 IPv6 주소를 표현할 수 있다.

```rust
enum IpAddrKine {
    v4,
    v6,
}

let four = IpAddrKind::v4;
let six = IpAddrKind::v6;
```

이런 식으로 열거형을 정의할 수 있다. 이러면 이제 `IpAddrKind`는 코드 어디에서나 쓸 수 있는 커스텀 데이터 타입이 된다.

또 다른 예시로 `qiskit`이라는 양자 컴퓨팅 프레임워크에서 양자 게이트 관련 상태들이 이런 식으로 열거형으로 정의되어 있다.

열거형을 사용하면 코드를 간결하게 만들 수 있는데

```rust
enum IpAddr {
    V4(String),
    V6(String),
}

let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
```

이 코드를 구조체로 해결하려고 하면 꽤나 길어진다.

### Option 열거형

`Option` 열거형은 값이 있거나 없을 수도 있는 상황을 나타낸다. 널로 써도 될 것 같지만, 널은 런타임에 오류를 발생시킬 수 있기 때문에 `Option` 열거형을 사용한다.

요약하자면 널 값을 널이 아닌 것처럼 사용하려고 하면 여러 종류 에러가 나타날 수 있다는 것인데, 이것은 개념적인 문제에서 나타난다기보다 구현적인 문제에 의해 나타난다.

러스트에는 널이 없다. 다만 값의 존재 또는 부재를 나타내는 `Option` 열거형이 있다. `Option<T>`로 나타낸다.

```rust
enum Option<T> {
    Some(T),
    None,
}
```

와 같이 **정의되어있다**. 다시 구현할 필요는 없다! 여기서 `T`는 제네릭 타입 매개변수로, 구체적인 `T`의 타입을 집어넣는 것이 `Option`이 어떤 타입의 값을 가질 수 있는지 나타낸다.`T` 자체에는 어떤 타입이든 들어갈 수 있다.

---

# 소유권

[러스트 소유권 이해하기](https://maximizemaxwell.com/rust-ownership) 를 참고할 수 있다.

## 소유권

러스트의 소유권 시스템은 메모리 안전성을 보장하는 핵심 개념이다. 소유권 규칙이라는 것이 존재하는데, 이는 다음과 같다:

1. 러스트에서 각각의 값은 소유자가 정해져 있다.
    
2. 한 값의 소유자는 동시에 여럿 존재할 수 없다.
    
3. 소유자가 스코프 밖으로 벗어날 때, 값은 버려진다.
    

여기서 3번에 스코프라는 것에 대해 나오는데, 스코프란 프로그램 내에서 아이템이 유효한 범위를 말한다. 러스트에서는 중괄호 `{}`로 묶인 블록이 스코프를 나타낸다. 중괄호가 끝나는 시점에서 러스트는 `drop`이라는 함수를 호출하여 해당 변수의 메모리를 해제한다. 즉, 스코프가 끝나면 해당 변수는 더 이상 유효하지 않게 된다.

예제로 보자면

```rust
{ // s가 선언되기 전이다. 아직 유효하지 않다
    let s = "Hello"; // s가 선언되어 여기서부터 유효하다
    // s로 어떠한 작업을 한다.
} // 스코프가 종료되어 s가 유효하지 않다.
```

이런 식으로 스코프가 종료되면 해당 변수는 더 이상 유효하지 않게 된다.

### String

바로 위에서 "Hello"라는 문자열을 사용했는데, 이는 문자열 리터럴로, 컴파일 타임에 결정되는 불변 문자열이다. 하지만 러스트에서는 동적으로 크기가 변할 수 있는 문자열을 다루기 위해 `String` 타입을 제공한다.

```rust
let s = String::from("Hello");
```

로 쓰면 가변적인 문자열을 만들 수 있다. 이 문자열은 힙에 저장되며, 런타임에 크기가 결정된다. 이런 이야기가 왜 나오나 싶지만 뒤의 소유권 개념에서 다시 나온다.

### 소유권 이동

```rust
let s1 = String::from("Hello");
let s2 = s1;
```

와 같은 코드를 작성했다고 하자. 그런데 `s2`에 `s1`을 할당하면 스택에 있는 데이터가 복사되지 포인터가 가리키는 힙 영역의 데이터까지 복사되지는 않는다.

그래서 `s1`이 여전히 살아있다면 `s1`과 `s2`가 같은 힙 메모리를 가리키게 된다. 이런 경우 중복 해제 문제가 발생할 수 있다.

그래서 러스트에서는 소유권 이동이라는 개념을 제공하는데, `s1`이 더 이상 유효하지 않고, `s2`만 유효하다고 판단하는 것이다.

위에서 소유권 규칙을 설명했는데, 한 값의 소유자는 동시에 여럿 존재할 수 없다는 규칙이 바로 이 소유권 이동을 통해 지켜진다. 즉, `s1`은 더 이상 유효하지 않게 되고, `s2`가 `s1`의 소유권을 가지게 된다.

이것은 복사와는 다른 개념이다. 스택 데이터만 복사되는 것을 얕은 복사, 힙 데이터까지 복사되는 것을 깊은 복사라고 하는데, 얕은 복사에서는 위와 같이 중복 해제 문제가 발생할 수 있다고 했다. 그러나 깊은 복사에서 힙 데이터까지 복사하는 것은 상당히 비효율적이 될 수 있다. 따라서 러스트에서는 `소유권 이동`을 통해 소유권을 이전하는 방식을 채택하고 있다.

힙 데이터까지 깊은 복사를 하려면 `clone` 메서드를 사용해야 한다.

```rust
let s1 = String::from("Hello");
let s2 = s1.clone(); // 깊은 복사
```

이런식으로.

그런데

```rust
let x = 1;
let y = x; // x의 값을 y에 복사
println!("x: {}, y: {}", x, y); // x와 y 모두 1
```

를 실행하면 여전히 x가 유효하다. 아까 우리가 살펴본 소유권 개념으로는 그래서는 안 될 것 같은데, 왜 그럴까?

그건 소유권 개념은 힙 메모리 관리에 사용되는 개념이므로 저렇게 컴파일 타임에 크기를 알 수 있는 고정 크기 값들은 스택에 저장되기 때문이다. 일반적인 스칼라 타입이나 튜플은 이런 식의 복사가 가능하다.

```rust

fn main() {
    let s1 = gives_ownership(); // gives_ownership이 반환 값을 s1으로
    let s2 = String::from("Hello"); // s2가 스코프 안으로

    let s3 = takes_and_gives_back(s2); // s2가 함수로 값을 이동
    // 함수도 값을 s3으로 이동
} // s3이 스코프 밖으로 벗어나면서 drop
// s1도 스코프 밖으로 벗어나고 버려진다

fn gives_ownership() -> String {
    // gives_ownership은 자신의 반환 값을 자신의 호출자 함수로
    // 이동시키게 된다
    let some_string = String::from("yours"); //some_string이 스코프 안으로

    some_string // some_string이 반환되고 호출자 함수로 이동
}

fn takes_and_gives_back(a_string: String) -> String {
    // a_string이 스코프 안으로
    a_string // a_string이 반환되고 호출자 함수로 이동
}
```

위 예제를 통해 소유권을 더 깊이 이해할 수 있다.

## 참조와 대여

만약 함수에 값을 넘겨주고 싶지만 소유권을 넘겨주고 싶지 않다면, 참조를 사용해야 한다. 참조는 `&` 기호를 사용하여 생성한다.

```rust
fn main() {
    let s1 = String::from("hello");

    let (s2, len) = calculate_length(s1);

    println!("The length of '{}' is {}.", s2, len);
}

fn calculate_length(s: String) -> (String, usize) {
    let length = s.len(); // len()은 String의 길이를 반환합니다

    (s, length)
}
```

이걸 어떻게 바꿀 수 있을까?

```rust
fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1); // s1의 참조를 넘겨줍니다
    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len() // s의 길이를 반환합니다
}
```

이런 식으로 `&`를 사용하여 참조를 넘겨줄 수 있다. 이 경우 소유권은 이동하지 않고, 함수 내에서 `s`의 값을 읽을 수만 있다.

### 가변 참조자

참조자도 `mut` 키워드를 사용하여 가변 참조자를 만들 수 있다. 가변 참조자는 값을 변경할 수 있는 참조자이다.

```rust
fn main() {
    let mut s = String::from("hello");

    change(&mut s); // 가변 참조자를 넘겨줍니다
    println!("Changed string: {}", s);
}

fn change(s: &mut String) {
    s.push_str(", world!"); // s의 값을 변경합니다
}
```

이런 식으로 가변 참조자를 사용하여 값을 변경할 수 있다. 그런데 가변 참조자는 큰 제약사항이 있는데, 어떤 값에 대해 가변 참조자가 있다면 **그 값에 대한 참조자는 더 이상 만들 수 없다.** 즉, 가변 참조자는 동시에 하나만 존재할 수 있다. 이는 데이터 경합을 방지하기 위한 것이다.

### 댕글링 참조

댕글링 참조는 참조자가 유효하지 않은 메모리를 가리키는 경우를 말한다. 러스트에서는 댕글링 참조를 방지하기 위해 컴파일 타임에 참조의 유효성을 검사한다.

```rust
fn main() {
    let reference_to_nothing = dangle();
}

fn dangle() -> &String {
    let s = String::from("hello");

    &s
}
```

이런 코드는 컴파일 에러를 발생시킨다. 왜냐하면 `s`는 함수가 끝나면 스코프 밖으로 벗어나면서 메모리가 해제되기 때문이다.

이걸 유효하게 하려면

```rust
fn no_dangle() -> String {
    let s = String::from("hello");

    s // 소유권을 반환합니다
}
```

이런 식으로 소유권을 반환해야 한다. 즉, 참조를 반환하는 것이 아니라 소유권을 반환하는 것이다. 이렇게 하면 `s`가 스코프 밖으로 벗어나면서 메모리가 해제되지 않고, `no_dangle` 함수의 호출자가 `s`의 소유권을 가지게 된다.

## 슬라이스

슬라이스는 배열이나 문자열의 전체를 참조하는 것이 아니라 그 일부를 참조하는 것을 말한다.

문자열 슬라이스의 예제를 살펴보자.

```rust
let s = String::from("hello world");
let hello = &s[0..5]; // "hello" 부분 문자열을 슬라이스로 참조
let world = &s[6..11]; // "world" 부분 문자열을 슬라이스로 참조
```

이런 식으로 문자열의 일부를 슬라이스로 참조할 수 있다.

---

# 제네릭 & 라이프타임

## 제네릭

제네릭을 사용하면 함수 시그니처나 구조체 아이템에 다양한 데이터 타입을 사용할 수 있다. 제네릭을 사용하여 아래 예제의 두 함수를 합칠 수 있다.

```rust
fn largest_i32(list: &[i32]) -> &i32 {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn largest_char(list: &[char]) -> &char {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}
```

이 두 함수를

```rust
fn largest<T> (list: &[T]) -> &T {
    let mut largest = &list[0];
    T:PartialOrd; // T가 PartialOrd 트레이트를 구현해야 한다.
    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}
```

이런 식으로 제네릭을 사용하여 하나의 함수로 합칠 수 있다. 여기서 `T`는 제네릭 타입 매개변수로, 함수가 호출될 때 구체적인 타입으로 대체된다. 다만 `T`가 `PartialOrd` 트레이트를 구현해야 한다는 제약 조건을 추가해야 한다. 이는 `>` 연산자를 사용할 수 있도록 하기 위함이다. `i32`와 `char`는 모두 `PartialOrd`를 구현하고 이므로 이 함수는 두 타입 모두에 대해 작동한다.

### 제네릭 구조체

```rust
struct Point<T> {
    x: T,
    y: T,
}

fn main() {
    let integer_point = Point { x: 5, y: 10 };
    let float_point = Point { x: 1.0, y: 4.0 };

    println!("Integer Point: ({}, {})", integer_point.x, integer_point.y);
    println!("Float Point: ({}, {})", float_point.x, float_point.y);
}
```

이런식으로 제네릭 구조체를 정의할 수 있는데 여기서 x, y는 모두 같은 타입이어야만 컴파일 에러가 발생하지 않는다. 만약 서로 다른 타입을 사용하고 싶다면

```rust
struct Point<T, U> {
    x: T,
    y: U,
}

fn main() {
    let integer_point = Point { x: 5, y: 10.0 };
    let float_point = Point { x: 1.0, y: 4.0 };

    println!("Integer Point: ({}, {})", integer_point.x, integer_point.y);
    println!("Float Point: ({}, {})", float_point.x, float_point.y);
}
```

이런 식으로 제네릭 타입 매개변수를 두 개 사용하여 서로 다른 타입을 사용할 수 있다.

### 제네릭 열거형

제네릭 열거형도 마찬가지로 정의할 수 있다. 이전에 `Option` 열거형을 살펴봤는데, 이 또한 제네릭 열거형이다.

```rust
enum Option<T>{
    Some(T),
    None,
}
```

다른 예제로

```rust
enum Result<T, E> {
    Ok(T),
    Err(E),
}
```

도 존재한다. 이 열거형은 성공적인 결과와 실패한 결과를 나타내는 데 사용된다. `T`는 성공적인 결과의 타입, `E`는 실패한 결과의 타입을 나타낸다.

### 제네릭 메서드

제네릭 메서드도 정의할 수 있다. 예를 들어

```rust
struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn new(x: T, y: T) -> Point<T> {
        Point { x, y }
    }

    fn x(&self) -> &T {
        &self.x
    }

    fn y(&self) -> &T {
        &self.y
    }
}

fn main() {
    let p = Point::new(5, 10);
    println!("Point: ({}, {})", p.x(), p.y());
}
```

## 트레이트

트레이트는 특정한 타입이 가지고 있으면서 다른 타입과 공유할 수 있는 기능을 정의한다. 이전에 `Debug`도 트레이트의 일종이다.

아래 코드는 `candle`의 [`qwen3.rs`](http://qwen3.rs)에서 가져온 예제이다. 이런 식으로 기본 트레이트인 `Debug`과 `Clone`같은 것은 꽤 자주 쓰이는 편이다.

```rust
#[derive(Debug, Clone)]
pub(crate) struct Qwen3RotaryEmbedding {
    sin: Tensor,
    cos: Tensor,
}

impl Qwen3RotaryEmbedding {
    pub(crate) fn new(dtype: DType, cfg: &Config, dev: &Device) -> Result<Self> {
        let dim = cfg.head_dim;
        let max_seq_len = cfg.max_position_embeddings;
        let inv_freq: Vec<_> = (0..dim)
            .step_by(2)
            .map(|i| 1f32 / cfg.rope_theta.powf(i as f64 / dim as f64) as f32)
            .collect();
        let inv_freq_len = inv_freq.len();
        let inv_freq = Tensor::from_vec(inv_freq, (1, inv_freq_len), dev)?.to_dtype(dtype)?;
        let t = Tensor::arange(0u32, max_seq_len as u32, dev)?
            .to_dtype(dtype)?
            .reshape((max_seq_len, 1))?;
        let freqs = t.matmul(&inv_freq)?;
        Ok(Self {
            sin: freqs.sin()?,
            cos: freqs.cos()?,
        })
    }

    /// Apply RoPE (q, k shape: B x H x L x D)
    pub(crate) fn apply(&self, q: &Tensor, k: &Tensor, offset: usize) -> Result<(Tensor, Tensor)> {
        let (_, _, seq_len, _) = q.dims4()?;
        let cos = self.cos.narrow(0, offset, seq_len)?;
        let sin = self.sin.narrow(0, offset, seq_len)?;
        let q_embed = candle_nn::rotary_emb::rope(&q.contiguous()?, &cos, &sin)?;
        let k_embed = candle_nn::rotary_emb::rope(&k.contiguous()?, &cos, &sin)?;
        Ok((q_embed, k_embed))
    }
}
```

다시 돌아와서 트레이트를 정의하는 방법을 알아보자.

트레이트는 `trait` 키워드를 사용하여 정의한다. 예를 들어

```rust
pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(Read more...)")
    }
}
```

이런 식으로 정의하고

```rust
let article = NewsArticle {
    headline: String::from("Breaking News"),
    location: String::from("New York"),
    author: String::from("John Doe"),
    content: String::from("Lorem ipsum dolor sit amet..."),
};

println!("New article available! {}", article.summarize());
```

이런 식으로 사용할 수 있다.

## 라이프타임으로 참조자의 유효성 검증하기

라이프타임은 어떤 참조자가 필요한 기간동안 유효함을 보장하도록 하는 일종의 제네릭이다.

라이프타임의 주 목적은 댕글링 참조 방지다. 예를 들어

```rust
fn main() {
    let r;
{
        let x = 5;
        r = &x; // x는 이 블록 안에서만 유효하다
    }
    println!("r: {}", r); // r은 x를 참조하지만 x는 이미 스코프 밖으로 벗어났다
}
```

이런 코드는 컴파일 에러를 발생시킨다. 그런데 이때 컴파일러의 에러 메시지를 살펴보면 `'x' does not live long enough`라는 메시지가 나타난다.

## 대여 검사기(borrow checker)

러스트 컴파일러는 대여 검사기라는 기능을 통해 참조자의 유효성을 검사한다. 대여 검사기는 참조자가 유효한지, 댕글링 참조가 발생하지 않는지 등을 검사한다.

```rust
fn main() {
    let r;                // ---------+-- 'a
                          //          |
    {                     //          |
        let x = 5;        // -+-- 'b  |
        r = &x;           //  |       |
    }                     // -+       |
                          //          |
    println!("r: {}", r); //          |
}                         // ---------+
```

위 예제의 라이프타임을 시각적으로 나타내면 위와 같다. 여기서 `'a`는 `r`의 라이프타임, `'b`는 `x`의 라이프타임을 나타낸다. `r`은 `x`를 참조하지만, `x`는 스코프 밖으로 벗어나면서 더 이상 유효하지 않게 된다. 따라서 `r`은 댕글링 참조가 되어 컴파일 에러가 발생한다. 그러니까 참조 대상이 참조자보다 오래 살지 못해서 러스트 컴파일러가 이 프로그램을 허용하지 않는 것이다.
