01. 기초 - Rust

Shawn Kang·2023년 2월 14일
0

Rust

목록 보기
2/2
post-thumbnail

변수와 상수

일반 변수

let mut val: i32 = 1239;

변수 할당은 let으로 가능하고, mut로 가변 여부를 지정할 수 있다.

또한 변수 이름 뒤에 콜론과 함께 자료형을 입력하여 변수의 자료형을 명시적으로 지정해줄 수 있다. 다만 대부분의 상황에서 Rust 컴파일러가 알아서 자료형을 추측하기 때문에 보통은 생략하고, 문자열을 파싱하는 등 다양한 자료형이 올 수 있는 애매한 상황에서만 명시적으로 지정해줘도 된다.

상수

const THIS_IS_CONSTANT_VALUE: f64 = 1929.2323;
const THIS_IS_CONSTANT_VALUE: f64 = 3.14 * 23123;

상수는 const로 가능하고, Rust에서는 대문자 + 밑줄(_)을 활용하여 상수 이름을 정한다. 그리고 일반 변수와 다르게 반드시 상수의 자료형을 명시적으로 정의해주어야 한다! 또한 상수는 런타임에서 연산이 불가능하다. 굳이 상수에 식을 써야겠다면, 사칙연산 같은 가장 기초적인 연산만 사용할 수 있다는 듯.

Shadowing

let a = 6;
let a = 7;

저번 글에서 간단히 짚고 넘어갔기 때문에 뭐... 굳이 더 설명할 필요는 없을 것 같다.



자료형

Rust의 자료형에는 단일 값을 받는 Scalar 자료형과 여러 값을 하나로 묶는 Compound 자료형이 있다고 한다. Scalar에는 정수, 소수(부동소수점), 부울, 문자가 존재하고, Compound에는 튜플과 배열이 있다.

정수

// 정수의 다양한 표현
let dec = 1000;
let hex = 0xff;
let oct = 0o77;
let bin = 0b11;

// 언더바 사용
let easy_to_read = 1_024_768; // 1,024,768

8, 16, 32, 64, 128 비트의 Signed, Unsigned 정수형을 제공한다. 앞에 부호 여부를 쓰고 그 뒤에 비트 수를 붙여주면 된다. u32, i32, ..., 이런 식으로. 추가로 isize, usize의 경우 시스템의 최대 메모리 공간에 맞추는 듯하다. 32 비트 시스템의 경우는 32 비트로, 64 비트 시스템의 경우는 64 비트로. 추가로 byte라는 자료형도 있던데, 이거는 아스키 코드 말하는 것 같다.

당연히 진법도 적용 가능하다. 그리고 신기한 점으로 보통 사람들이 숫자를 쓸 때 읽기 쉽게 쉼표를 중간에 붙이곤 하는데(1,024,768처럼), Rust에서는 쉼표의 역할을 하는 밑줄을 제공해준다. 근데 실제로 자주 쓰진 않을 듯.

let mut a = b'l';
a = a.

그리고 정수 연산 중 발생하는 예외 처리를 위한 다양한 함수가 있다고 한다. wrapping_addoverflow_add 이런 것들... wrapping_add를 예로 들면 자료형이 받아낼 수 있는 최대값 이상이 연산될 경우, 그 결과값에 2의 보수법을 적용한 값을 결과로 반환한다고 한다. 다만 이거는 오류를 내지 않기 위한 수단일 뿐, 실제 프로그램 돌아갈 땐 의도되지 않은 결과가 발생할 게 뻔하므로 예외 처리를 빡세게 할 수 있도록 하자.

소수

let a = 3.14; // f64 

f32, f64의 두 가지 자료형을 제공한다고 한다. 타입 지정 안 하고 소수를 쓸 경우, 기본값은 f64.

또한 얘는 Signed밖에 없다.

숫자 자료형의 연산

더하기, 빼기, 나누기, 곱하기, 나머지 연산 다 지원한다.

부울

true 그리고 false.

이건 뭐 코드 좀 만져본 사람이면 다 인정하는 거라.

문자 (char)

let character = 'a';

4 바이트의 크기를 갖는 유니코드 형식 문자다. 이러한 특징으로 인해 한국어나 이모티콘까지 모두 char에 해당한다.

튜플

// 선언
let tup = (1, 3.1415, "Alpha");

// 튜플 내 값에 접근
println!("{}", tup.0);

Python에서 보던 그거다. 위 코드처럼 소괄호에 쉼표를 더해 선언할 수 있고, 각자 다른 자료형끼리도 하나의 튜플로 묶을 수 있다.

튜플 내부 값에 대한 접근은 tuple.n의 형태로 한다. 예를 들어 tuple의 10 번째 값을 확인하고 싶다면 tuple.9라고 써 주면 된다. (인덱스가 0부터 시작하기 때문에 9로 표기)

// 튜플 값의 수정
let mut a = (1, 3, 4);
println!("{}", a.0);

a.0 = 3;
println!("{}", a.0);

다만 값의 수정이 불가능한 Python과 달리, Rust에서는 mut를 붙여 선언할 경우 값의 수정이 가능한 것으로 보인다. 위 코드는 컴파일 잘 되고 값도 바뀐 값으로 출력된다.

// 튜플 값의 분해
let tup = (1, 2, 3);
let (a, b, c) = tup

Rust의 튜플은 분해도 가능하다. 위 코드를 참고하자. 예시로 atup.0에 대응된다.

// Unit 튜플
let tup = ();

비어 있는 튜플은 특별히 Unit이라는 이름으로 불린단다. Kotlin에서 자주 보던 그거. 아마 C 계열의 void나 Kotlin의 Unit에 대응되는 요소 같다.

배열

// 선언
let arr = [1, 2, 3, 4, 5];

// 배열 내 값에 접근
println!("{}", arr[0]);

배열은 C의 배열과 거의 유사하다. 선언 형태도 똑같다.

튜플과 달리 단일 자료형의 값으로만 구성할 수 있고 대괄호와 인덱스를 통해 내부 값에 접근한다. 또한 길이가 고정되어 있다! 길이가 가변적인 Python의 리스트와는 다르다. Vector 자료형도 내부 라이브러리에 존재하기는 한다는데 아직은 안 보여줬고 나도 찾아보지는 않았다.

// 배열 자료형 명시하기
let integer_arr: [i32; 5] = [1, 2, 3, 4, 5];

// 하나의 값으로 배열 전체 초기화하기
let same_arr:[i32; 5] = [128; 5];

let으로 배열을 선언할 때 자료형을 명시하고 싶다면, [자료형; 길이]로 써 주자.



함수

Statement와 Expression

  • Statement 특정 동작을 수행하지만 반환 값 없음
  • Expression 특정 값으로 평가되어 반환될 수 있음

Statement의 예시로 let, fn 등이 있고, Expression은 뒤에 세미콜론이 붙지 않는다. 세미콜론이 있는 경우 이는 Statement라고 한다.

{
	let x = 3;
    x + 1
}

이상의 코드는 세미콜론이 붙지 않으므로 Expression이다. 대충 예상은 하겠지만 4를 반환한다.

함수

// 함수 예시
fn add_on_pie(value: f64) -> f64 {
	value + 3.14
}

// 함수
fn 함수_이름(매개변수: 매개변수 자료형) -> 반환 자료형 {
    함수 바디
}

함수는 위 코드와 같이 선언한다. Kotlin의 함수 형태와 거의 유사하다.

// Expression을 활용한 함수 반환
// 1. return과 세미콜론 다 붙이기: 컴파일 됨
fn some_func() -> u32 {
	return 120;
}

// 2. 세미콜론만 빼기: 컴파일 됨
fn some_func() -> u32 {
	return 120
}

// 3. return과 세미콜론 다 빼기: 컴파일 됨
fn some_func() -> u32 {
	120
}

위의 Expression을 활용하면 이런 다양한 반환문을 사용할 수 있다. 이게 가능한 이유는 Rust의 모든 함수는 암시적으로 함수 몸체의 가장 마지막 Expression을 반환하도록 구성되어 있기 때문이라고 한다. 신기한 부분.



흐름 제어

주석

//로 쓰면 된다. /* */도 된다.

///은 컴파일러에서 쓰지 말라고 한다.

반복

// loop
loop {
	break; // 루프 깨기
}

// while
while condition {

}

// for
for item in array {

}

loop, while, for이 있다. 그리고 셋 모두 breakcontinue를 지원한다.

loop의 경우 단순한 무한 반복이다.

while의 경우 위 코드의 condition에 해당하는 값이 true일 경우에만 반복한다. 재밌는 점 하나로 condition Expression을 소괄호에 묶어줄 필요가 없다. C는 소괄호에 안 묶어주면 오류 내는데. 이건 편리한 부분 같다.

for의 경우 Python에서 많이 본 형태대로 사용 가능하다.

// 평범한 탈출
loop {
	break;
}

// 값을 반환하며 탈출
let a = loop {
	break 300;
}

break 뒤에 특정한 값을 붙여 줄 경우 루프를 깨면서 그 값을 반환할 수 있다. 이상의 코드에서 a의 값은 300이 된다.

// 루프에 레이블 붙이기
'loop_1: loop {
	'loop_2: loop {
    	// body
    }
} 

루프에 이름도 붙여줄 수 있다. 작은 따옴표 뒤에 이름을 적어주고 루프를 선언해주면 된다. 이거는 보통 중첩 루프문에서 특정 루프를 깨고 싶을 때 사용하는 것 같다.



잡담

대충 코드 끄적이면서 몇 가지 특징을 알게 되었다. 간단히 정리해보자.

let tup = (1, 2, 3);
println!("{} {} {}", tup.0, tup.1, tup.2); // "1 2 3" 출력됨

먼저, String에 복잡한 표현식을 쓰고 싶을 때는 중괄호를 적극 이용하자. C 언어에서 문자열에 변수 넣을 때 썼던 방법과 유사하지만, 여기서는 순서만 잘 지켜주면 된다.

다음으로 Rust에는 후위표기식이 없단다. 후위표기식을 코드에 넣어 봤더니 "우리 집에는 그런 메뉴 없어요~"란다. 난 세상에 이런 기능 없다고 컴파일러가 말하는 경우는 처음 봤다. 재미있는 언어다.

마지막으로 Rust에서 Kotlin의 IntRange 같은 게 존재한다. 1..4 이런 식으로 쓰는데, Kotlin과는 다르게 마지막 값은 포함하지 않는다. 알아둬야 할 듯.

profile
i meant to be

0개의 댓글