Rust for the impatient

Nitroblue 1·2025년 8월 25일

10분만에 공부해보는 러스트 언어

  • variable binding
let x;
x = 42

is equal to
let x = 42;


  • Type annotation(주석)
let x: i32;		# i32 : 부호가 있는 32비트 정수, 마치 java의 int와 같은 느낌.
x = 42

let x: i32 = 42;

rust에서는 타입을 명시하지 않을 경우, 기본값으로 i32를 선언한다.


  • You can't access uninitialized variables
let x;
foobar(x);
x = 42;

it leads to runtime error. so, you have to initialize variables before access.

let x;
x = 42;
foobar(x)

  • underscore(밑줄) : 기본적으로 무언가를 사용하지 않고 버릴 때 경고가 표시되지 않도록 한다.
    escape hedge가 있는 엄격한 기본값 패턴에서 지나치게 성가시지 않도록 Rust에서는 많이 쓴다.
let _ = 42;		# this does nothing because 42 is a constant
let _ = get_thing();	# this calls get_thing but throws away its result

  • pair
let pair = ('a', 17);
pair.0;		// this is 'a'
pair.1;		// this is 17

let pair: (char, i32) = ('a', 17)
rust에서는 보통 사용 중인 타입에 대해 잘 추론하기 때문에 명확히 지정해줘야 하는 경우는 드물다.


  • tuples
    부분으로 분해될 수 있다.
let (some_char, some_int) = ('a', 17);
assert!(some_char, 'a');
assert!(some_int, 17);

let (l, r) = slice.split_at(middle);
let (_, right) = slice.split_at(middle);	# 오른쪽 부분만 가져오는 식

  • 세미콜론... 그래.
let x = vec![1,2,3,4,5,6,7,8]
	.iter()
    .map(|x| x + 3)
    .fold(0, |x, y| x + y);

위 예시처럼 span될 수도 있다.


  • fn
# void fn
fn greet() {
	println!("Hi there!");
}

# integer returning fn
fn fair_dice_roll() -> i32 {
	4
}

  • pair of brackets
    자체 범위를 가진 블록을 선언한다.
  • tail
let x = {
	let y = 1;
    let z = 2;
    y + z	// ths is the 'tail'
}

  • match
    it is also an expression, not a statement.
fn fair_dice_roll() -> i32 {
	match feeling_lucky {
    	true => 6,
        false => 4,
    }
}

반드시 전부 일치해야 한다.

fn print_number(n: number) {
	match n.value {		//아래처럼 deeply nested 구조도 가능.
    	1 => println!("one"),
        2 => println!("two"),
        _ => println!("{}", n.value),	// _는 패턴 상관 안쓴다는 뜻.
    }
}

  • ::
    네임스페이스를 확실히 보여주는 방식.
    let least = std::cmp::min(3, 8);
    여기서 std는 crate(library), cmp는 source file, min(3, 8)은 function을 의미한다.

  • use
    다른 네임스페이스 범위에서의 이름을 가져올 수 있다.
use std::cmp::min;
let least = min(7,1);

rust는 엄격한 범위 규칙이 있기 때문에, 소스 코드에서 볼 수 없는 경우 사용할 수 없다.

type도 name space이다.

let x = "amos".len();	// 4
let x = str::len("amos");	// also 4

  • Struct
    it is declared with the struct keyword.
    원하는 새로운 타입을 만들 수 있다는 것 같다. JAVA랑 비슷한 구석이 매우 많은듯.
struct Number {
	odd: bool,
    value: i32,
}

they can be initialised using literals:

let x = Number { odd: false, value: 2 };
let y = Number { value: 3, odd: true };
// 즉, 순서는 중요치 않다.

struct Number {
	odd: bool,
    value: i32,
}

impl Number {
	fn is_positive(self) -> bool {
    	self.value > 0
    }
}

let minus_two = Number {
	odd: false,
    value: -2,
}
println!("{}", minus_two.is_positive());

struct는 JAVA의 클래스와 같은 역할이구나.
위 예시에서 Number가 클래스이며, 해당 속성들 내에서 상속받는 느낌.

! 기본적으로 불변이기때문에 내부 변경 불가

let n = Number {
	odd: true,
    value: 17,
};
n.odd = false;

=> error: cannot assign to 'n.odd', as 'n' is not declared to be mutable.


  • mut
    mut makes a variable binding mutable:
    rust는 C와 다르게 mut에 대해 엄격하다.
let mut n = Number {
	odd: true,
    value: 17,
}
n.value = 19	// all good.

  • Functions can be generic:

    프로그래밍에서 제네릭(Generic) 문법에서 사용되는 형식 매개 변수(Type Parameter)로, 특정 데이터 타입을 '일반화'하여 코드의 재사용성과 유연성을 높이는 데 사용됩니다. T는 Type의 약자이며, 이 변수를 통해 클래스나 메서드가 어떤 특정 타입의 데이터를 다룰지 외부에서 지정할 수 있도록 합니다.

    1. 타입 일반화:
      다양한 데이터 타입에 대해 동일한 기능을 수행하는 클래스나 메서드를 만들 수 있습니다. 예를 들어, 특정 타입을 지정하는 대신 다양한 타입의 객체를 저장할 수 있는 '박스' 클래스를 만들 수 있습니다.
    2. 재사용성과 유연성:
      코드를 간결하게 하고 재사용성을 높여줍니다. 각 타입마다 다른 버전을 만들 필요 없이, 단일 제네릭 클래스 또는 메서드를 통해 여러 데이터 형식을 처리할 수 있습니다.
    3. 타입 안정성 증가:
      컴파일 시점에 타입 체크가 이루어져, 런타임 시 발생할 수 있는 형변환 오류를 줄여줍니다.
    4. 개발 생산성 향상:
      IDE가 해당 객체의 타입을 인지하기 때문에 자동 완성 기능 등을 통해 개발 생산성을 높여줍니다.
    5. 용도 예시
      (1) 제네릭 클래스:
      클래스 이름 뒤에 < T >를 붙여 해당 클래스가 특정 타입을 일반화된 T로 다룬다는 것을 명시합니다.
      (2) 제네릭 메서드:
      메서드 자체에 < T >를 선언하여 해당 메서드가 특정 타입의 데이터를 다룰 수 있도록 합니다.
    6. 다른 제네릭 타입 매개 변수
      T는 가장 흔하게 사용되는 약자이지만, K(Key), V(Value), E(Element) 등 다른 약자를 사용하여 각자의 의미를 부여하기도 합니다.
struct Pair<T> {
	a: T,
    b: T,
}

let p1 = Pair { a: 3, b: 9 };	// = Pair<i32>
let p2 = Pair { a: true, b: false };	// = Pair<bool>

  • vec
    힙에 할당되는 배열이다. 따라서 런타임동안 배열 전체 용량에 도달하면 더 큰 배열로 바꿔준다.
fn main() {
	let v1 = vec![1,2,3];
    let v2 = vec![true, false, true];
}

  • enum (열거형)
enum Result<T, E> {
	0k(T),
    Err(E),
}

  • expect
    에러가 발생했을 때 메시지 유도 가능.
les s = str::from_utf8(&[195, 40])
.expect("valid utf-8");

-> error 발생시

thread 'main' panicked at 'valid utf-8:
'Utf8Error{ valid_up_to: 0, error_len: Some(1) }''


  • error handling codes
let melon = &[240, 159, 141, 137];
match str::from_utf8(melon) {
	ok(s) => println!("{}", s),
    Err(e) => panic!(e),
}

if let ok(s) = str::from_utf8(melon) {
	println!("{}", s),

match std::str::from_utf8(melon) {
	ok(s) => println!("{}", s),
    Err(e) => return Err(e),
}
ok(())	// (assuming inside 'fn -> Result<(), std::str::Utf8Error>')

let s = str::from_utf8(melon)?;


  • iteration(반복자)
    let natual_numbers = 1..; // 무한대까지 간다.
    반복자는 지연 계산되기 때문에 RAM에 저장될 수 있다.
arrange 사용하는 경우
// 0 or greater
(0..).contains(&100);		// true
// 20 or less than 20
(..=20).contains(&20));		// true
// only 3, 4, 5
(3..6).contains(&4));		// true

반복 가능한 모든 게 for loop에 사용될 수 있다.

fn main() {
	for i in vec![52,49,21] {	// vec도 가능
    	println!("I like number {}", i);	// I like number 52 / ~ 49 / ~ 21
    }
    
    for i in &[52, 49, 21] {	// slice도 가능
    	println!("I like number {}", i);	// I like number 52 / ~ 49 / ~ 21
    }
    
    for c in "rust".cahrs() {
    	println!("get {}", c);	// get r / get u / get s / get t
    }
}
fn main() {
	for c in "SuPRISE INbOUND".chars()
    	.filter(|C| c.is_lowercase())
        .flat_map(|c| c.to_uppercase()) {
        print!("{}", c);		// output: UB
    }
}

0개의 댓글