기본 변수는 불변성이다.
안전성과 손쉬운 동시성이라는 장점을 취할 수 있도록 강제하는 요소 중 하나
가변성 변수를 선언하고 싶다면 접두어로 mut을 추가한다.
fn main() {
let x = 5;
println!("The value of x is: {}", x);
x = 6;
println!("The value of x is: {}", x);
}
error[E0384]: re-assignment of immutable variable `x`
--> src/main.rs:4:5
|
2 | let x = 5;
| - first assignment to `x`
3 | println!("The value of x is: {}", x);
4 | x = 6;
| ^^^^^ re-assignment of immutable variable
에러가 나타내는 것은 불변셩 변수의 재할당이고, 원인은 불변성 변수 x
에 두 번째로 값을 할당했기 때문이다.
가변성 예시
fn main() {
let mut x = 5;
println!("The value of x is: {}", x);
x = 6;
println!("The value of x is: {}", x);
}
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
Running `target/debug/variables`
The value of x is: 5
The value of x is: 6
mut
을 사용하는 것이 허용되지 않는다. 상수는 기본 설정이 불변성인 것이 아니고 불변성 그 자체이기 때문이다.let
키워드 대신 const
키워드를 사용해야 하고, 값의 유형을 선언해야 한다.// Rust의 상수 명명 규칙에 따라 모든 단어를 대문자로 사용
const MAX_POINTS: u32 = 100_000;
let
키워드를 사용해서 다음처럼 반복하여 같은 변수명으로 변수를 shadow 할 수 있다.fn main() {
let x = 5;
let x = x + 1;
let x = x * 2;
println!("The value of x is: {}", x);
}
// The value of x is: 12
mut
으로 선언하는 것과는 차이가 있다.let
키워드를 사용하지 않고 변수에 새로 값을 대입하려고 하면 컴파일시에 에러가 발생한다.mut
과 shadowing의 차이는 let
키워드를 다시 사용하여 효과적으로 새 변수를 선언하고, 값의 유형을 변경할 수 있으면서도 동일 이름을 사용할 수 있다는 점이다.let spaces = " ";
let spaces = spaces.len();
spaces
변수가 문자열 유형이고 두 번째 spaces
변수는 첫 번째 것과 동일한 이름을 가진 새롭게 정의된 숫자 유형의 변수이기 때문이다.mut
을 사용하려고 했다면 다음과 같은 에러를 얻게 될 것이다.let mut spaces = " ";
spaces = spaces.len();
error[E0308]: mismatched types
--> src/main.rs:3:14
|
3 | spaces = spaces.len();
| ^^^^^^^^^^^^ expected &str, found usize
|
= note: expected type `&str`
found type `usize`
Rust에서 사용되는 모든 값들은 어떤 타입을 갖는다.
타입은 크게 스칼라와 컴파운드, 둘로 나눌 수 있다.
String
을 parse
를 사용하여 숫자로 변환하는 것처럼 타입의 선택 폭이 넓은 경우는 반드시 타입의 명시를 첨가해야 한다.let guess: u32 = "42".parse().expect("Not a number!");
error[E0282]: type annotations needed
--> src/main.rs:2:9
|
2 | let guess = "42".parse().expect("Not a number!");
| ^^^^^
| cannot infer type for `_`
| consider giving `guess` a type
-(2^(n-1) - 1)
~ 2^(n-1) - 1
까지의 값을 포괄한다.0
~ 2^n - 1
까지의 값을 저장할 수 있다.i32
가 일반적으로 좋은 선택이다.f32
와 f64
가 있으며 각기 32bit와 64bit의 크기를 갖는다.f64
이다.fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}
fn main() {
// addition
let sum = 5 + 10;
// subtraction
let difference = 95.5 - 4.3;
// multiplication
let product = 4 * 30;
// division
let quotient = 56.7 / 32.2;
// remainder
let remainder = 43 % 5;
}
bool
로 명시된다.true
와 false
의 두가지fn main() {
let t = true;
let f: bool = false; // with explicit type annotation
}
char
는 가장 근본적인 알파벳 타입char
타입은 작은따옴표로 사용x fn main() { let c = 'z'; let z = 'ℤ'; let heart_eyed_cat = '😻';}
char
타입은 Unicode Scalar를 표현하는 값이고 이는 ASCII 보다 많은 표현을 가능하게 한다.fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
tup
에는 튜플 전체가 bind 된다.fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup; // x: 500, y: 6.4, z: 1
println!("The value of y is: {}", y);
}
tup
에 bind 시킨다.let
을 통해 tup
을 세개의 분리된 변수 x, y, z에 이동시킨다.6.4
이다.마침표(.)
뒤에 접근하길 원하는 값의 색인을 넣는 것으로 튜플의 요소에 직접적으로 접근할 수 있다.fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0; // 500
let six_point_four = x.1; // 6.4
let one = x.2; // 1
}
fn main() {
let a = [1, 2, 3, 4, 5];
}
// 변하지 않을 고정된 길이
let months = ["January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"];
fn main() {
let a = [1, 2, 3, 4, 5];
let first = a[0]; // first: 1
let second = a[1]; // second: 2
}
fn main() {
let a = [1, 2, 3, 4, 5];
let index = 10;
let element = a[index];
println!("The value of element is: {}", element);
}
$ cargo run
Compiling arrays v0.1.0 (file:///projects/arrays)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/arrays`
thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is
10', src/main.rs:6
note: Run with `RUST_BACKTRACE=1` for a backtrace.
fn
은 새로운 함수의 선언을 가능하게 한다.Rust의 변수나 함수 이름 규칙은 snake_case이다. - 모든 문자는 소문자를 사용하여 밑줄 표시로 단어 구분
fn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("Another function.");
}
// Hello, world!
// Another function.
fn
으로 시작하며 함수 이름 뒤에 괄호의 형식으로 되어 있다.another_function
을 main
함수 앞에 정의해도 된다.fn main() {
another_function(5);
}
fn another_function(x: i32) {
println!("The value of x is: {}", x);
}
// The value of x is: 5
another_function
의 선언은 x
로 명명된 하나의 매개변수를 갖는다.x
의 타입은 i32
로 정의된다.5
가 another_function
으로 전달되면 println!
매크로는 중괄호 짝으로 된 형식 문자열에 5
를 전달한다.fn main() {
another_function(5, 6);
}
fn another_function(x: i32, y: i32) {
println!("The value of x is: {}", x);
println!("The value of y is: {}", y);
}
// The value of x is: 5
// The value of y is: 6
another_function
은 각각 i32
타입인 두 개의 매개변수를 갖는 함수이다.fn main() {
let y = 6; // 구문
}
let
구문을 사용해서는 다른 변수에 값을 대입할 수 없다.fn main() {
let x = (let y = 6);
}
$ cargo run
Compiling functions v0.1.0 (file:///projects/functions)
error: expected expression, found statement (`let`)
--> src/main.rs:2:14
|
2 | let x = (let y = 6);
| ^^^
|
= note: variable declaration using `let` is a statement
let y = 6
구문은 반환 값이 없으므로 x
에 bind 시킬 것이 없다.fn main() {
let x = 5;
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {}", y);
}
4
를 산출합니다. let
구문의 일부로 y
에 bound됩니다. x + 1
줄의 마지막이 세미콜론으로 끝나지 않은 점을 주목하세요. 표현식은 종결을 나타내는 세미콜론을 사용하지 않습니다.->
)뒤에 선언해야 한다.return
키워드와 값을 써서 함수로부터 일찍 반환할 수 있지만, 대부분의 함수들은 암묵적으로 마지막 표현식을 반환한다.fn five() -> i32 {
5
}
fn main() {
let x = five();
println!("The value of x is: {}", x);
}
// The value of x is: 5
five
함수가 반환한 값이고, 이 때문에 반환 타입을 i32
로 한것이다.let x = five();
줄은 반환 값을 변수의 초기 값으로 사용하는 것을 보여준다. 또한 five
의 반환 값이 5
이기 때문에 해당 줄은 다음과 동일하다.let x = 5;
five
함수는 매개변수 없이 반환 값에 대한 타입만 정의되어 있지만, 본문에는 5
만이 세미콜론 없이 있는 이유는 값을 반환하고자 할 때 사용하는 표현식이기 대문이다.fn main() {
let x = plus_one(5);
println!("The value of x is: {}", x);
}
fn plus_one(x: i32) -> i32 {
x + 1
}
// The value of x is: 6
x + 1
끝에 세미콜론을 추가하여 표현식을 구문으로 변경하면 다음과 같은 에러를 얻는다.
error[E0308]: mismatched types
--> src/main.rs:7:28
|
7 | fn plus_one(x: i32) -> i32 {
| ____________________________^
8 | | x + 1;
| | - help: consider removing this semicolon
9 | | }
| |_^ expected i32, found ()
|
= note: expected type `i32`
found type `()`
plus_one
함수의 정의는 i32
값을 반환하겠다고 하였으나 구문은 값을 산출하지 않기에 ()
처럼 비어있는 튜플로 표현된다.프로그래머는 메모를 남기거나 소스코드에 컴파일러는 무시하도록 되어 있는 주석을 사용한다.
Rust에서 주석은 두개의 슬래쉬
//
로 시작해야 하고 해당 줄의 끝까지 계속된다.
// Hello, world.
// 우리는 여기에 뭔가 복잡한 것을 적어놓고자 하는데, 그를 위해 충분히 긴 여러 줄의 주석이 필요합니다.
// 휴! 다행입니다.
// 이 주석은 그에 대해 설명할테니까요.
fn main() {
let lucky_number = 7; // I’m feeling lucky today.
}
if
표현식은 코드가 조건에 따라 분기할 수 있게 한다.
fn main() {
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
// condition was true
if
표현식은 if
란 키워드로 시작하며 뒤이어 조건이 온다.number
가 5보다 작은 값을 가지는지 여부가 된다.if
식의 조건과 관련된 코드 블럭은 갈래(arms)로 불린다.else
식을 포함시킬 수 있는데, 이는 조건이 거짓으로 산출될 경우 실행시킬 코드 블럭을 프로그램에 제공한다.else
식을 제공하지 않는데 조건이 거짓이 되면, 프로그램은 if
블록을 생략하고 다음 순서의 코드를 실행하게 된다.number
의 값을 let number = 7;
와 같이 7로 변경하면 condition was false
가 출력된다.bool
이어야 한다.fn main() {
let number = 3;
if number {
println!("number was three");
}
}
error[E0308]: mismatched types
--> src/main.rs:4:8
|
4 | if number {
| ^^^^^^ expected bool, found integral variable
|
= note: expected type `bool`
found type `{integer}`
fn main() {
let number = 3;
if number != 0 {
println!("number was something other than zero");
}
}
// number was something other than zero
if
와 else
사이에 else if
식을 추가 결합하여 다양한 조건을 다룰 수 있다.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");
}
}
// number is divisible by 3
if
가 표현식이기 때문에 let
구문의 우측에 사용할 수 있다.fn main() {
let condition = true;
let number = if condition {
5
} else {
6
};
println!("The value of number is: {}", number);
}
// The value of number is: {5}
if
와 else
갈래는 모두 i32
정수형을 결과 값으로 가진다.fn main() {
let condition = true;
let number = if condition {
5
} else {
"six"
};
println!("The value of number is: {}", number);
}
error[E0308]: if and else have incompatible types
--> src/main.rs:4:18
|
4 | let number = if condition {
| __________________^
5 | | 5
6 | | } else {
7 | | "six"
8 | | };
| |_____^ expected integral variable, found reference
|
= note: expected type `{integer}`
found type `&str`
if
블록이 정수형을 산출하는 식이고 else
블록은 문자열을 산출하는 식이다.number
변수의 타입이 뭔지 확실히 정의해야 한다.number
가 사용되는 모든 곳에서 유효한지 검증할 수 있기 때문에number
의 타입을 실행 시에 정의되도록 할 수 없다.loop
keyword는 Rust에게 그만두라고 명시하여 알려주기 전까지 코드 블럭을 반복 수행한다.
fn main() {
loop {
println!("again!");
}
}
// 프로그램을 강제 정지하기 전까지 again!이 반복 출력된다.
// ctrl + c 를 통해 정지가능
break
keyword를 위치시켜 프로그램이 언제 루프를 멈춰야 하는지 알려줄 수 있다.break
를 호출하여 반복을 정지시킨다.fn main() {
let mut number = 3;
while number != 0 {
println!("{}!", number);
number = number -1;
}
println!("LIFTOFF!!!");
}
// 3!
// 2!
// 1!
// LIFTOFF!!!
while
구조자를 통해 배열과 같은, 콜렉션의 각 요소에 걸쳐 반복 수행 할 수 있다.fn main() {
let a = [10, 20, 30, 40, 50];
let mut index = 0;
while index < 5 {
println!("the value is: {}", a[index]);
index = index + 1;
}
}
// the value is: 10
// the value is: 20
// the value is: 30
// the value is: 40
// the value is: 50
for
반복문을 사용한다.fn main() {
let a = [10, 20, 30, 40, 50];
for element in a.iter() {
println!("the value is: {}", element);
}
}
// the value is: 10
// the value is: 20
// the value is: 30
// the value is: 40
// the value is: 50
while
구조자를 사용한 것과 같은 결과를 볼 수 있다.Range
는 한 숫자에서 다른 숫자 전까지 모든 숫자를 차례로 생성한다.rev
메소드는 range를 역순한다.fn main() {
for number in (1..4).rev() {
println!("{}!", number);
}
println!("LIFTOFF!!!");
}
// 3!
// 2!
// 1!
// LIFTOFF!!!
https://rinthel.github.io/rust-lang-book-ko/ch03-00-common-programming-concepts.html