일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- Proxy Resource
- 비구조화 할당
- 자바스크립트
- grafana
- 러스트
- elasticsearch
- vgw
- Kubernetes
- docker
- 구조분해 할당
- Endpoints
- docker swarm
- On-Premise
- JavaScript
- Custom Resource
- Site-to-Site VPN
- AWS
- 온프레미스
- null 병합 연산자
- optional chaining
- 단축 평가
- api gateway
- 옵셔널 체이닝
- cognito
- prometheus
- rust
- VPC
- transit gateway
- Await
- Service
- Today
- Total
만자의 개발일지
[Rust] 변수와 타입 본문
Rust
Rust는 메모리 안전성과 성능 및 병렬 처리에 중점을 둔 프로그래밍 언어입니다.
러스트의 가장 큰 특징 중 하나는 소유권(ownership)과 수명(lifetime) 이라는 개념을 통해 GC없이 컴파일 타임에 메모리 안정성을 보장하기 때문에 예측 가능한 코드를 작성할 수 있고 GC를 사용하는 다른 언어에 비해 런타임에 발생하는 오버헤드가 작다는 장점이 있습니다.
위 개념들은 추후에 알아보도록 하고 이번 포스팅에서는 러스트의 변수 선언 방식과 데이터 타입에 대해서 알아보도록 하겠습니다.
변수(Variables)
Rust에서는 let 키워드를 사용하여 변수를 선언합니다.
다음과 같이 변수를 선언하여 사용할 수 있습니다.
fn main() {
let x = 1;
println!("x is {x}"); // x is 1
}
가변성(Mutability)
let 키워드로 선언한 변수는 기본적으로 불변(immutable)입니다.
여기서 불변이라는 건 변수에 값을 할당한 후에 해당 변수의 값을 변경할 수 없다는 의미입니다.
let 키워드로 선언된 변수의 값을 변경하려고 하면 컴파일러는 다음과 같은 에러를 출력할 것입니다.
fn main() {
let x = 1;
println!("x is {x}"); // x is 1
x = 2; // error
println!("x is {}", x);
}
여기서 Rust의 장점이 나오는데 컴파일러는 단순히 에러만 출력해주는 것이 아닌, 어느 부분을 어떻게 수정해야 코드가 정상적으로 동작하는지도 알려줍니다.
이는 개발자가 디버깅하는데 있어서 매우 유용한 기능입니다.
컴파일러는 "불변으로 선언된 변수에 값을 재할당할 수 없다"라는 에러를 출력합니다.
그리고 재할당하고싶으면 "mut 키워드를 고려해봐" 라고 알려줍니다.
mut 키워드를 사용하여 기본적으로 불변인 변수를 가변(mutable)으로 만들 수 있습니다.
mut 키워드는 다음과 같이 변수명 앞에 붙여서 사용할 수 있습니다.
fn main() {
let mut x = 1;
println!("x is {x}"); // x is 1
x = 2;
println!("x is {}", x); // x is 2
}
러스트가 기본적으로 변수를 불변으로 선언하는 이유는 위에서 소개한 러스트의 장점과 연관이 있습니다.
러스트는 안정성을 중요시하는데, 변수를 불변으로 선언하는 것을 기본값으로 제공함으로써 예기치 않은 변경을 방지하고, 더 나은 안정성을 보장하게됩니다.
또한 변수의 값을 변경하고 싶을 때 mut 키워드를 명시적으로 사용하게 유도(nudge)함으로써 개발자의 의도를 명확하게 표현할 수 있습니다.
상수(Constant)
러스트에서 상수는 기본적으로 불변이 아닌, 항상 불변입니다. mut 키워드를 사용할 수 없고, 변수의 타입이 반드시 명시되어있어야 합니다. 상수는 const 키워드로 선언할 수 있습니다.
const DAYS_IN_WEEK: u8 = 7;
fn main() {
println!("days in week: {}", DAYS_IN_WEEK); // days in week 7
}
Rust는 스네이크 케이스 방식의 네이밍 컨벤션을 사용합니다.
상수는 고정값이나 단일값처럼 프로그램의 여러 부분에서 필요한 부분을 선언할 때 유용하며, 선언된 스코프 내에서 프로그램이 종료되기 전까지 항상 유효합니다.
섀도잉(Shadowing)
Rust에서는 이전 변수명과 같은 이름으로 새 변수를 선언하여 기존 변수를 덮어씌울 수 있습니다.
이를 가려졌다(shadowed)라고 표현하며, 아래와 같이 let 키워드를 사용해 똑같은 이름의 변수를 선언하여 변수를 가릴 수 있습니다.
fn main() {
let mut x = 10;
println!("The value of x is: {x}"); // 10
{
let x = x * 2; // shadowing
println!("The value of x in the inner scope is: {x}"); // 20
}
x = x + 5;
println!("The value of x is: {x}"); //15
}
섀도잉은 선언된 스코프 내에서만 유효합니다. 섀도잉을 통해 x 를 불변 변수로 선언하여 해당 스코프에서 x 의 값을 변경할 수 없게 하였는데 이를 freezing 이라고 합니다.
스코프가 끝나면 섀도잉이 종료되면서 x 는 기존 값인 10 으로 돌아오는 것을 보실 수 있습니다.
섀도잉과 mut 키워드의 차이점은 섀도잉은 let 키워드를 사용하여 새로운 변수를 선언하는 것이기 때문에 다른 타입의 값을 할당할 수 있지만 mut 키워드는 같은 타입에 한에서만 값을 변경할 수 있습니다.
fn main() {
let a = 10;
let a = "Rust"; // shadowing
let mut b = 10;
b = "Rust"; // mismatched types
}
타입(Data Types)
Rust에는 타입은 크게 기본 타입(primitive types)과 커스텀 타입(custom types), 2가지 타입으로 구분할 수 있습니다.
기본 타입(Primitive Types)
기본 타입은 정수형(i32, u16, ...), 부동소수점(f64, f128, ...), 불리언, 문자열, 포인터, 함수, 배열, 튜플 등등 Rust에 기본적으로 내장된 타입 전체를 가르킵니다. 기본 타입에 대한 정보는 아래 공식문서를 통해 확인하실 수 있습니다.
std - Rust
§The Rust Standard Library The Rust Standard Library is the foundation of portable Rust software, a set of minimal and battle-tested shared abstractions for the broader Rust ecosystem. It offers core types, like Vec and Option , library-defined operations
doc.rust-lang.org
기본 타입 안에서도 두 가지 타입으로 분류할 수 있는데 바로 스칼라 타입(Scalar Types)과 복합 타입(Compound Types) 입니다.
스칼라 타입(Scalar Types)
스칼라 타입은 다음과 같이 하나의 값만 저장하는 타입들을 말합니다.
타입 | 예시 |
정수형(integer) | i32, u8, i64 ...등 |
부동소수점형(floating-point) | f32, f64 ... 등 |
불리언(boolean) | bool (true, false) |
문자(character) | char ('a', '가', '😊') |
fn main() {
let i: i32 = -1;
let u: u32 = 1; // unsigned integer
let f: f64 = 3.3;
let b: bool = true;
let c: char = '😊';
}
복합 타입(Compound Types)
복합 타입은 다음과 같이 여러 개의 값을 한꺼번에 저장하는 타입들을 말합니다.
타입 | 예시 |
튜플(tuple) | (i64, f32, char) |
배열(array) | [1, 2, 3, 4, 5] |
fn main() {
let tup: (i32, f64, u8) = (-500, 6.4, 1);
let (x, y, z) = tup;
let arr = [1, 2, 3, 4, 5];
println!("The value of z is: {}", z);
println!("The first element of arr is: {}", arr[0]);
}
커스텀 타입(Custom Types)
기본 타입 이외에도 개발자가 직접 새로운 타입을 정의할 수 있는데 이를 커스텀 타입이라고 부릅니다.
커스텀 타입에는 2가지 타입이 있습니다.
구조체(Struct)
구조체는 여러 변수들을 하나로 묶어주는 타입입니다.
각각의 변수명과 타입을 필드라고 부르며, 필드들은 서로 다른 타입을 가질 수 있으며, . 연산자를 통해 구조체 필드에 접근할 수 있습니다.
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 3, y: 7 };
println!("Point is at ({}, {})", p.x, p.y);
}
열거형(Enum)
열거형은 다양한 형태의 값(variant)들을 하나로 묶어주는 타입입니다.
열거형은 상태나 특정 값을 정의할 때 주로 사용하며, 후에 설명할 match 키워드를 통해 각 값에 대한 특정 동작을 정의할 수 있습니다.
enum Color {
Red,
Green,
Blue,
}
fn main() {
let my_color = Color::Green;
match my_color {
Color::Red => println!("The color is Red!"),
Color::Green => println!("The color is Green!"),
Color::Blue => println!("The color is Blue!"),
}
}
타입 별칭(Type Alias)
타입 별칭은 기존 타입에 새로운 이름을 정의할 수 있습니다.
긴 타입명을 짧게 줄인다거나 타입에 의미를 부여할 때 자주 사용합니다.
type Kilometers = i32;
fn main() {
let x: i32 = 5;
let y: Kilometers = 10;
println!("total distance: {} km", x + y);
}
타입 변환(Type Casting)
Rust에서는 as 키워드를 사용하여 변수의 타입을 변환할 수 있습니다.
fn main() {
let x: i32 = 5;
let y: f64 = x as f64 + 0.1; // 정수형 -> 부동소수점형으로 변환
println!("y: {}", y);
}
as 키워드는 주로 간단한 타입 변환에 사용됩니다.
다만 변환 과정에서 에러가 발생해도 강제로 변환시키기 때문에 데이터 손실이 발생할 수 있다는 단점이 있습니다.
fn main() {
let x: i32 = 257;
let y: u8 = x as u8; // i32 -> u8로 변환
println!("y: {}", y); // y: 1, u8 범위(0~255)므로 overflow 발생, 에러 발생하지 않음
}
타입 변환에서 발생하는 에러를 처리하고 싶은 경우 try_from() 이라는 함수를 사용하여 처리할 수 있지만 이 포스팅에서 설명하고자 하는 범위를 벗어나기 때문에 추후에 작성하도록 하겠습니다.
이번 포스팅에서는 러스트의 변수와 가변성 그리고 데이터 타입에 대해 알아보았습니다.
다음 포스팅에서는 함수와 표현식에 대해 알아보도록 하겠습니다.
참고
'Rust' 카테고리의 다른 글
[Rust] 열거형 (0) | 2025.05.18 |
---|---|
[Rust] 구조체와 메서드 & 연관 함수 (0) | 2025.05.04 |
[Rust] 조건문과 반복문 (0) | 2025.05.02 |
[Rust] 함수와 표현식 (0) | 2025.04.29 |