'rust'에 해당되는 글 1건

  1. 2022.12.08 Rust~~~!!!
카테고리 없음2022. 12. 8. 13:50

먼가 힙한 언어가 나왔다. 만들기 시작한지는 20년가까이 되었지만 실제 사용이 되기 시작한지는 5년여가 된듯 하다.

 

이걸 또 알아바야 겠다.

 

마스코드가 꽃게모양인데 rust사용자를 Rustancean이라고 부른다.

 

영문공식문서 : https://doc.rust-lang.org/book/title-page.html

한글공식문서 : https://rinthel.github.io/rust-lang-book-ko/foreword.html

현행화가 한글이 부족하다고 한다. 영문이 먼저 업데이트 되고 한글이 업데이트 될텐데 잘 안되나 보다.

위 문서가 러스트 프로그래밍 공식 가이드 라는 서적으로 출간되어 있는데 오역도 많아서 안보는게 낳다고 한다.

 

신생언어인만큼 많은 변경이 계속 있어왔고 앞으로도 변경이 있을 수 있기 때문에 안바뀔것 같은것 위주로 알아보고자 한다.

 

Graydon Hoare(그레이던 호어)라는 모질라 개발자가 2006년에 개인 프로젝트로 시작되었다고 한다. 이 후 모질라가 공식적으로 후원을 하여 2015년에 비로소 1.0이 세상에 나왔다고 한다. 참 대단한게 개인이 이런걸 시작하고 또 회사에서 여기에 투자를 할 수 있는 문화가 너무 부럽다. 회사나 개발자나 참 프로답다는 생각이 든다.. 그에 비해 나는.. ㅎㅎㅎ;;;

 

이 후 많은 인기를 누렸고 여러 서비스들에서 사용이 되었다. discord의 서버의 상당부분이 rust로 되어 있다고 한다.

 

주요한 특징은 다음과 같다.

 

  • 안정성을 추구한다.
  • 병렬성을 추구한다.
  • 현대적이다(Modern language)

 

GC?

 

컴파일언어로서 특이한 특징을 갖는다.

GC를 살펴보자면 다른 언어들의 GC는 GC collector가 메모리를 계속 감시하면서 조건이 맞게 되면 메모리를 해제하는 방식으로 동작한다.

Rust는 컴파일타임에 메모리를 정리하는 코드를 삽입한다. C언어는 직접 메모리를 할당하고 직접 해제해야 하는데 이런 오류를 줄여주기 위해 컴파일러가 직접 해당 코드를 삽입한다는 것이다. 무슨 AI가 코딩하는것 같다. GC는 언제 일어날지 모르는데 반해 Rust는 컴파일타임에 이미 정해져 있다. 어쨌든 이런 방법으로 Null Pointer에러는 안녕이다.

 

소유권

 

여기에 소유권이라는 독특한 개념이 추가 되었다. 이는 모든 값에 ownership이라는 변수가 있다. 모든 값은 하나의 소유자만 있을 수 있고 소유자가 범위를 벗어나면 값이 삭제되면서 메모리에서 해제가 된다.

 

 

시작하기

 

Rust toolchain installer 다운로드 및 설치

$ curl —proto ‘=https’ —tlsv1.2 -sSf https://sh.rustup.rs | sh

 

rustc(컴파일러), cargo(패키지매니저)가 설치된다. 

Cargo는 빌드, 테스트, 문서화, 배포등 개발관련 모든 과정을 커버한다.

 

cargo new : 새 프로젝트를 생성하고 Cargo.toml에 디펜던시를 추가

cargo build : 프로젝트 빌드

cargo run : 컴파일및 실행

 

rustup으로 툴체인에 다양한 유틸리티를 추가해줄수 있다.

rustfmt: 코딩 스타일가이드에 맞춰주는 포매터

clippy: 코드상의 실수와 개선점을 제안

 

$ rustup update

$ rustup component add rustfmt clippy rls rust-analysis rust-src

 

fn main() {

  let x: i8 = 10;  // let으로 변수 선언

  let mut y: i8 = 20; // mut로 변경가능한 변수 선언

  y = y + 20;

  let z: i32 = 100; // i8은 8비트 정수, i32는 32비트 정수, f32: 부동소수, bool: 불리언타입

  println!{“{}”, x+y);

}

 

fn square(x: i32) -> i32 {

  x * x // 마지막 표현식이 리턴값이 된다. ;(세미콜론)을 넣지 않는다

}

 

let x = 1;

let y = { // 변수 선언에도 구문식 사용이 가능

  let x = 2;

  x +1

};

 

let y = if x == 1 {

  10

} else if x > 1 {

  20

} else {

  30

};

 

타언어의 switch같은 match키워드가 있다.

 

match x {

1 => println!(“one”),

2 => println!(“two”),

3 => println!(“three”),

_ => (),

}

 

 

Nullable

 

swift나 kotlin의 optional에 해당하는 Option<T>열거형이 제공된다.

 

enum Option<T> {

  Some(T),

  None,

}

 

null일 수 있는 변수는 Option<T> 타입으로 사용할 수 있다.

 

fn plus_one(x: Option<i32>) -> Option<i32> {

  match x {

    None => None,

    Some(i) => Some(i+1),

  }

}

 

let one: Option<i32> = Some(1);

let two = plus_one(one);

let three = plus_one(two);

 

너무나도 명시적으로 이놈은 null일수 있다는것을 알수 있게 설계되었다는것을 알 수 있다. swift나 kotlin이 rust의 Option을 따라한것 같은 느낌이 강하게 드는 부분이다.

 

 

오너쉽(Ownership)

 

메모리를 관리하기 위한 방법으로 GC에 비해 참 맘에 드는 부분이다. 

모든 값은 owner라는 변수를 갖고 한번에 하나의 오너만 가질수 있고 범위를 벗어나면 해제된다.

 

let x = 3;

let y = x;

 

x, y는 모두 프리미티브타입의 값으로 스택메모리에서 운영되며 x에 3이 바인딩되고 y에 그 값이 복사된다. x, y 모두 3이된다.

 

let s1 = String::from(“say hello”)

let s2 = s1

 

String::from은 let s = “hello”처럼 문자열 리터럴을 할당하지 않고 힙에 할당하게 된다. 따라서 런ㄹ타임에 문자열을 수정할 수 있다.

s2 = s1에서 s1을 s2에 할당했다. 이는 오너쉽이 변경됨을 의미한다. s1의 정보는 스택에 문자열이 할당된 포인터(ptr), 문자열길이(len), 용량(capacity)정보가 담기게 되는데 s2에 할당하는 순간 s2의 정보가 스택에 새로 생성되고 ptr, len, capacity도 복사가 된다. 이 때 s1은 무효화한다. 따라서 s2의 ptr만 실제 문자열의 포인터를 가리키게 된다. 이제 s2가 범위를 벗어날때 s2의 drop메소드가 호출되고 포인터도 해제되어 메모리가 수거되게 된다.

이는 함수의 인자로 넘어갈때도 마찬가지이며 함수의 반환값으로 넘길때도 마찬가지이다.

 

let s = String.from(“say hello”);

print_hello(s);

// 여기서 s는 유효하지 않기 때문에 사용할 수 없다. 

 

함수의 인자로 넘기되 오너십을 이동시키고 싶지 않을 때 는 참조만 넘겨준다. 이를 Borrowing(빌림)이라고 한다.

 

let s = “say hello”;

let len = get_length(&s);

 

fn get_length(s: &String) -> usize {

  s.len()

}

 

오너쉽을 넘기지 않기 때문에 s가 빌림 인자로 넘어갈때 스택에는 포인터만 저장되고 문자열의 길이와 용량정보는 저장되지 않는다. 그러면 함수내에서 참조가 가능하다. 

이 때 참조만 하지 않고 빌린값을 변경하고자 한다면 mut 를 사용해야 한다.

 

let mut hello = String::from(“hello”);

change(&mut hello);

println!(“{}”, hello);

 

fn change(s: &mut String) {

  s.push_str(“, world”);

}

 

change에서 문자열의 값을 변경 하였는데 실제로 힙에서 할당한 주소공간에서 마음대로 용량을 늘릴수 없기 때문에 새로운 메모리공간을 확보하여 처리하게 된다. 따라서 ptr값이 변경이 된다.

Rust는 위와 같이 아주 작은 양의 값을 할당할때도 메모리 재할당을 하지 않도록(성능저하를 발생시킴) 하기 위해 애초에 용량을 좀더 크게 잡아준다. “hello” 다섯 글자의 경우에도 넉넉히 30자 정도의 메모리 공간을 할당한다.

 

가변 참조를 빌려줄때 주의 할점은 한스코프안에서 한번만 가능하다는 것이다.

 

let r1 = &mut s;

let r2 = &mut s;

 

s의 가변참조를 r1과 r2에 할당했는데 이 때 먼저 수행한 r1의 가변참조는 무효화된다. 만일 이런식으로 코딩하면 컴파일 단계에서 걸러준다.

 

error[E0499]: cannot borrow `s` as mutable more than once at a time

 

 

Panic!

 

언어에 패닉이라는 키워드가 사용되다니.. 참 재미있게 느껴진다. 패닉의 뜻에서 느껴지듯이 굉장히 큰 문제가 발생했을때 임을 알 수 있다.

panic!(“메세지”)은 메세지를 출력하고 앱을 종료시킨다.

 

러스트는 또 Result 열거형을 제공한다.

 

enum Result<T, E> {

  Ok(T),

  Err(E),

}

 

어떤 함수가 Result를 반환하면 match로 분기처리할 수 있다.

 

let file = FIle::open(“some file”);

let file = match file {

  Ok(f) => f,

  Err(error) => panic!(“Failed to open the file: {:?}”, error)

};

 

여기까지 몇가지 특성을 알아 보았는데..

더 자세히는 공식문서를 보면서 알아보아야 할것이다.

 

Rust는 타깃이 성능향상에 맞춰져 있으며 따라서 고성능을 요하는 서버 시스템프로그램에 적당해 보인다.

 

안드로이드도 플랫폼에 Rust를 적용해 나가고 있다고 한다.

https://source.android.com/docs/setup/build/rust/building-rust-modules/overview?hl=ko

Posted by 삼스