소개

이 글은 Rust Tutorial 을 읽고 학습한 내용을 정리한 글입니다. 번역해주신 분께 감사드립니다.

목적

정말 간단하게, 문법 정도만 배워서 실제 서버를 만들어 보는 것이 목표이다.
언어에 대한 자세한 학습이나 심도 깊은 고민은 하지 않고 동작하는 코드를 작성할 수 있을 정도의 문법만 학습한다.

설치

Rust를 설치하기 위해선 여러가지 방법이 존재한다. 하지만 제일 간단한 방법은 아래 스크립트의 실행이다.

$) curl https://sh.rustup.rs -sSf | sh

$) source $HOME/.cargo/env ## 새로운 sh 세션을 열때는 필요없다.

Cargo

cargo는 패키지 매니저 및 빌드 시스템이다.

프로젝트 생성 및 생성된 프로젝트에 대한 빌드 및 실행, syntax check 등을 할 수 있다.

위의 설치 커맨드를 이용했다면 cargo는 이미 설치되어있다.

1)프로젝트 생성

$) cargo new hello_world  ## --bin 옵션은 default라 생략

이렇게 생성된 Project는 아래와 같이 구성된다.

- hello_world
 	- Cargo.toml ## 프로젝트에 대한 설명과 의존성 정보를 담고 있다.
 	- src
	 	- main.rs ## 실행점이 될 메인 파일

2)프로젝트 컴파일, 실행, syntax check

# cargo로 생성한 프로젝트에 위치한다고 가정한다.
$) caro build # 컴파일하여 바이너리 생성
$) cargo run # 컴파일 및 바이너리 실행 
$) cargo check # 컴파일에 문제가 없을지 체크, 바이너리 생성 안함

변수와 함수

변수

rust의 변수 선언은 다음과 같이 한다.

fn main() {
	let x: u32 = 5;
	println!("x is {}", x);
}

let이란 keyword를 통해 변수 선언을 알리고, 그 이후 변수의 이름을 적고 :(colon)뒤에 해당 변수의 타입을 명시한다. 그리고 =(assign)을 통해 값을 할당한다.
변수 타입은 명시적으로 선언하지 않아도 값을 통해 선언이 되는 것으로 보인다.
아직 이부분에 대해서는 동작하는 것만 확인하였고 위에 대해 확인하지는 않았다.

변수는 기본적으로 한번 ‘선언’ 되면 불변이다. 아래와 같이 이미 선언한 변수에 대해 값을 다시 할당하면 컴파일시 에러를 만날 것이다.

cannot assign twice to immutable variable
fn main() {
	let x = 5;
	x = 10;
}

사족이지만 대부분의 프로그래밍 랭귀지는 에러 메세지를 통해 무엇이 문제인지를 확인할 수 있다.

위에서 만난 에러 메세지처럼 rust는 기본적으로 변수는 불변이다.
하지만 다음과 같은 keyword mut를 이용하여 변수의 값을 변경할 수 있다.

fn main() {
    let mut y = 10;
    println!("Now y is {}, y);
    y = 20;
    println!("Then, Now y is {}", y);
}

다만 다음과 같은 shadowing을 통해 같은 이름의 변수를 다시 선언하고 값을 재사용할 수 는 있다.

fn main() {
    let x = 5;
    println!("1. The value of x is: {}", x); 

    let x = x * 2;
    println!("3. The value of x is: {}", x); 
}

컴파일 및 실행시켜보면 컴파일 에러 없이 실행이 되며 x의 값이 5, 10과 같이 바뀌는 것을 볼 수 있다.

상수 선언은 변수와 다른 keyword const를 사용한다.

const HELLO_MESSAGE: &str = "Hello World";
println!("{}", HELLO_MESSAGE);

상수는 변수와는 다르게 타입을 항상 선언해 주어야 한다.

데이터 타입

rust가 지원하는 데이터 타입에는 크게는 스칼라 타입, 복합 타입이 존재한다.

  • 스칼라 타입
    • 부호 있는 정수, 부호 없는 정수
      • u32, i32, …
    • 부동 소수정
      • f32, …
    • 유니코드 캐릭터
      • char
    • boolean
      • bool

더 상세한 정보는 rust-lang 에서 볼 수 있다.

fn main() {
    let unsigned_int: u32 = 1024;
    println!("unsigned_int value is {}", unsigned_int);

    let signed_int: i32 = -1024;
    println!("signed_int value is {}", signed_int);

    let float_number: f32 = 0.123;
    println!("float_number value is {}", float_number);

    let char_variable_kor: char = '가';
    println!("char_variable_kor value is {}", char_variable_kor);

    let char_variable_eng: char = 'a';
    println!("char_variable_eng value is {}", char_variable_eng);

    let bool_variable: bool = true;
    println!("bool_variable value is {}", bool_variable);
}

  • 복합 타입
    • array
      • [1,2,3]
      • 배열의 요소는 같은 타입만 가능하다.
    • tuple
      • (1, true)
      • 다양한 타입의 요소를 포함시킬 수 있다.
fn main() {
    let tup: (u32, i32, bool) = (1024, -1024, false);
    let ( x, y, z ) = tup; // need to destruct
    println!("tuple value is x:{}, y:{}, z:{}", x, y, z);

    let array = [1,2,3];
    println!("array[0] is {}", array[0]);

}

함수

함수 선언은 다음과 같이 할 수 있다.

fn 함수이름(paramter_name: parameter_type) -> return_type {
  statements;
  expression;
}

함수를 정의할 때 parameter, return_type는 필수는 아니다. 하지만 parameter를 사용한다면 해당 parameter의 type은 항상 정의해주어야 하고 값을 리턴한다면 return_type 역시 필수로 정의해 주어야 한다.

  • statement vs expression
    • statement (구문)
      • 값을 반환하지 않는 동작
    • expression (표현식)
      • 값을 반환하는 동작
fn main() {}
	let x = 5; // statement. x will be 5
    let y = {
		let x = 3;
        x + 1
    }; // expression. y will be 4
}

y의 값은 4가 될 것이다. expression의 안에 마지막 줄인 x + 1에는 semi-colon이 없다. semi-colon이 없음이 바로 expressions을 끝낸다는 의미이며, 해당 값이 expression의 결과로 반환되며, 항상 이 반환 값을 각져야 한다.

아직까지 이 expression과 statement의 차이를 알아야 하는게 왜 중요한지, 그리고 이것이 rust에서 어떤 중요점을 갖는지는 명확히 알지 못한다.

여기서 알아간 것은 return value는 semi-colon이 없어야 한다 와 expression을 통해 변수를 정의할 수 있다 이다.

제어문

if expression

if 조건문은 다음과 같은 문법으로 사용된다.

if 조건 {
	statement || expression
} else if {

} else {

}

조건은 boolean이 되는 값으로, 괄호로 둘러쌓이지 않는다.
즉 조건의 값이 1이나 0의 숫자일때 이를 boolean으로 캐스팅하지 않는다.
당연하지만 else if와 else는 if 조건문을 사용하는데 있어서 필수는 아니다.

fn main() {
    let number = 3;

    if number > 0 {
        println!("number greater than zero");
    }
}

if 는 expression이므로 let 뒤에 사용할 수 있다.

fn main() {
    let number = 5;

    let result = if number > 4 { 
        true
    } else {
        false
    };  

    println!("value is {}", result);
}

참고로 if에 의해 할당되는 값의 타입은 항상 동일해야 하며 여러개일땐 컴파일시 에러가 발생한다.

루프

반복문 수행을 도와주는 keyword는 loop, while, for 가 제공된다.

  • loop
    • 종료 시그널(ctrl+c) 를 받거나 break를 만나기 전까지 계속 반복문을 수행한다.
      ex)
    loop {
      println!("Again N Again");
    }
  • while
    • while 조건문은 boolean으로 이것이 참일동안 계속 수행한다. break를 만나도 정지한다.
      ex)
fn main() {
    let mut num = 3;

    while num < 10 {
        println!("number is {}", num);
        num = num + 1;
    }
}
  • for
    • for 조건문은 콜렉션의 각 요소를 다 순회하면 종료된다.
      ex)
fn main() {
    let num = [1,2,3,4,5];

    for el in num.iter() {
        println!("el is {}", el);
    }
}

참고

- https://rinthel.github.io/rust-lang-book-ko/ch03-00-common-programming-concepts.html
- https://doc.rust-lang.org/book/ch03-00-common-programming-concepts.html
- https://doc.rust-lang.org/rust-by-example/primitives.html

realjays

반박시 당신말이 맞습니다.