반응형

■ 구조체

구조체는 튜플과 유사하게 구조체의 구성요소들을 각각 다른 타입으로 가질 수 있습니다.

튜플은 구성요소에 접근하기 위해 색인을 이용하지만, 구조체는 각 구성요소들을 명명함으로써 구성요소에 접근할 때 순서에 의존하지 않고 접근할 수 있습니다.


◆ 구조체 인스턴스 생성

Person 구조체를 정의하고 인스턴스를 생성하여 모든 구성요소를 출력합니다.

구조체가 구성요소에 접근할 때는 튜플과 동일하게 점(.)을 이용합니다.

struct Person{
    name : String,
    age : u32,
    address : String,
    email : String,
}

fn main(){
    // 인스턴스 생성
    let person = Person{
        name : String::from("Hyunmin han"),
        age : 29,
        address : String::from("서울시 강남구 역삼동"),
        email : String::from("hyunmin@gmail.com"),
    };
    
    // 구성요소에 접근
    println!("Name : {}", person.name);
    println!("Age : {}", person.age);
    println!("Address : {}", person.address);
    println!("Email : {}", person.email);
}

▶ 출력 결과

Name : Hyunmin han
Age : 29
Address : 서울시 강남구 역삼동
Email : hyunmin@gmail.com

◆ 구조체의 필드명과 변수명이 같을 경우 변수명으로 초기화 가능

구조체의 필드명과 변수명이 같을 때는 필드명 : 값 형식이 아닌 변수명 으로 초기화 할 수 있습니다.

struct Person{
    name : String,
    age : u32,
    address : String,
    email : String,
}

fn build_person(name : String, age : u32, address : String, email : String) -> Person{
    // 필드와 변수명이 같을 때 초기화
    Person { 
        name, 
        age, 
        address, 
        email 
    }
}

fn main(){
    let person = build_person(
        String::from("Hyunmin han"),
        29,
        String::from("서울시 강남구 역삼동"),
        String::from("hyunmin@gmail.com")
    );

    println!("Name : {}", person.name);
    println!("Age : {}", person.age);
    println!("Address : {}", person.address);
    println!("Email : {}", person.email);
}

▶ 출력 결과

Name : Hyunmin han
Age : 29
Address : 서울시 강남구 역삼동
Email : hyunmin@gmail.com

◆ 구조체의 갱신

A 인스턴스를 활용하여 B 인스턴스를 생성할 때 A 인스턴스의 필드를 그대로 사용해야 할 경우 구조체의 갱신법을 사용할 수 있습니다.

struct Person{
    name : String,
    age : u32,
    address : String,
    email : String,
}

fn main(){
    let person1 = Person{
        name : String::from("Hyunmin han"),
        age : 29,
        address : String::from("서울시 강남구 역삼동"),
        email : String::from("hyunmin@gmail.com"),
    };

    let person2 = Person{
        name : String::from("Donghun lee"),
        age : 28,
        // person1의 address, email 필드를 그대로 가져와서 사용
        ..person1
    };

    println!("Name : {}", person2.name);
    println!("Age : {}", person2.age);
    println!("Address : {}", person2.address);
    println!("Email : {}", person2.email);
}

▶ 출력 결과

Name : Donghun lee
Age : 28
Address : 서울시 강남구 역삼동
Email : hyunmin@gmail.com

◆ 튜플 구조체

구조체에 필드명을 명명하지 않고 타입만을 정의하여 사용합니다.

튜플의 데이터 접근 방식과 동일하게 색인(인덱스)으로 데이터에 접근합니다.

struct Location(i32, i32, i32);

fn main(){
    let top = Location(100, 100, 100);
    let bottom = Location(0, 0, 0);

    println!("Top : X : {}, Y :{}, Z :{}", top.0, top.1, top.2);
    println!("Bottom : X : {}, Y :{}, Z :{}", bottom.0, bottom.1, bottom.2);
}

▶ 출력 결과

Top : X : 100, Y :100, Z :100
Bottom : X : 0, Y :0, Z :0
반응형
반응형

■ 참조자와 빌림

참조자와 빌림은 해당 스코프에서 다른 로직으로 소유권을 넘기지 않고 참조자를 넘김으로써 소유권을 유지하고자 할 때 사용합니다.

 

여기에서 소유권이 생소하게 느껴질 수 있습니다. 러스트는 다른 프로그래밍 언어들과 비교하면 소유권이라는 특징으로 보다 효율적으로 메모리 관리가 가능합니다. 이러한 특징은 프로그래머의 메모리 관리를 통해 프로그램을 보다 효율적으로 관리 가능하게 합니다.

 

* 소유권에 대한 자세한 내용은 링크를 참고해 주세요. 


◆ 소유권의 이동 경로 확인

변수 s 의 소유권 이동경로는 아래와 같습니다.

  • main 함수
  • take_ownership 함수
  • take_ownership 함수 로직이 종료되면서 s의 메모리도 해제

s의 소유권 이동 경로에서 알 수 있듯이 println!("main fn : {}", s) 에서는 s의 소유권이 main 함수 스코프에 없기 때문에 borrow of moved value: `s` 에러가 발생합니다.

fn take_ownership(s : String){
    println!("take_ownership fn : {}", s);
}// 스코프 밖으로 벗어나면서 s의 메모리는 해제된다.

fn main(){
    let s = String::from("Owner Check");

    // s 에 소유권를 take_ownership 함수 스코프로 이동시킴
    take_ownership(s);

    // 에러 발생
    println!("main fn : {}", s);
}

◆ 참조자와 빌림으로 소유권 유지

s 변수의 소유권을 main 스코프에서 유지하면서 println!("main fn : {}", s) 를 정상적으로 실행 시킬 수 있는 방법은 참조자(&)를 활용하여 기존 스코프에서 소유권을 유지하는 것입니다.

fn take_ownership(s : &String){
    println!("take_ownership fn : {}", s);
}

fn main(){
    let s = String::from("Owner Check");

    // s 의 참조자를 take_ownership에 매개변수로 넘김
    take_ownership(&s);

    // s 의 소유권 유지
    println!("main fn : {}", s);
}

▶ 출력 결과

take_ownership fn : Owner Check
main fn : Owner Check
반응형
반응형

■ 소유권

소유권은 러스트의 핵심 기능으로써, 모든 프로그램은 실행하는 동안 컴퓨터의 메모리를 사용하는 방법을 관리해야 합니다. 러스트가 아닌 다른 몇몇 언어들은 프로그램이 실행될 때 더 이상 사용하지 않는 메모리를 끊임없이 찾는 가비지 콜렉션을 갖고 있으며, 이는 프로그래머가 직접 명시적으로 메모리를 할당하고 해제해야 합니다. 이와 달리 러스트의 메모리는 컴파일 타임에 컴파일러가 체크할 규칙들로 구성된 소유권 시스템을 통해 관리됩니다. 따라서 소유권 기능들은 런타임 비용이 발생하지 않습니다.

소유권을 잘 이해하기 위해 소유권의 규칙과 스택(Stack), 힙(Heap) 메모리에 역할과 기능에 대해 알아둘 필요가 있으며, 아래에서 간단히 확인해 보겠습니다.


◆ 소유권 규칙

  • 러스트의 각각의 값은 해당값의 오너라고 불리는 변수를 갖습니다.
  • 하나의 오너만 존재할 수 있습니다.
  • 오너가 스코프 밖으로 벗어날 때, 값이 버려집니다.

◆ 스택(Stack)

  • 런타임에 사용할 수 있는 메모리 부분입니다.
  • 모든 데이터는 고정된(불변) 크기를 갖습니다.
  • 후입선출(LIFO : Last In First Out) 자료구조 형식을 따릅니다.
  • 데이터에 접근하는 방식이 빠릅니다.

◆ 힙(Heap)

  • 런타임에 사용할 수 있는 메모리 부분입니다.
  • 컴파일 타임에 크기가 결정되어 있지 않거나 변경될 수 있는 데이터를 저장합니다.
  • 힙에 데이터를 넣을 때는 운영체제에 의해 공간을 할당 받고, 해당 공간의 포인터를 스택에 저장합니다.
  • 힙에 저장된 데이터에 접근할 때 저장 방식에 역순으로 스택에서 포인터를 통해 힙 공간으로 접근합니다.
  • 데이터에 접근하는 방식이 스택에 비해 느립니다.
  • 힙 데이터를 관리하는 것이 소유권의 존재 이유입니다.

 

반응형
반응형

■ 반복문

반복문은 반복적으로 일련의 작업을 수행해야할 때 사용합니다. Rust에서 제공하는 반복문은 loop, while, for 이며, 사용자의 기호에 맞게 보다 효율적인 작업이 가능하다고 판단되는 반복문을 사용하면 됩니다.


◆ loop

loop는 while, for문 과는 달리 조건 처리가 없기 때문에 break와 같이 로직을 벗어날 수 있는 처리를 하지않으면 무한 루프에 빠지게 됩니다. 따라서 loop는 로직안에서 조건문과 break 를 이용하여 조건 처리가 필요합니다.

 

예시) count에 1씩 더하는 작업을 반복적으로 진행하다가 count가 5가 되었을 때 loop 를 벗어나는 로직을 통해 loop에 대해 이해할 수 있습니다.

fn main() {
   let mut count = 0;

   loop{
       println!("count : {}", count);

       // count 가 5일 경우 break
       if count == 5{
            break;
       }
       // 1 씩 증가
       count += 1;
   }
}

▶ 출력 결과

count : 0
count : 1
count : 2
count : 3
count : 4
count : 5

◆ while

while은 조건이 참일 경우 반복 작업하고 조건이 거짓일 경우 로직을 벗어납니다.

 

예시) count에 1씩 더하는 작업을 반복적으로 진행하다가 count가 6이 되었을 때 조건이 거짓이 되고, 로직을 벗어나게 되는 예시를 통해 while의 로직을 확인할 수 있습니다.

fn main() {
   let mut count = 0;

   while count <= 5{
        println!("count : {}", count);
       
        // 1 씩 증가
        count += 1;
   }
}

▶ 출력 결과

count : 0
count : 1
count : 2
count : 3
count : 4
count : 5

◆ for

for문은 배열, 튜플 등 순서가 있는 데이터의 첫번째 요소부터 마지막 요소까지의 갯수 만큼 반복 작업을 수행합니다.

 

예시) count 배열의 요소에 값을 순차적으로 출력하는 예시를 통해 for의 로직을 확인할 수 있습니다.

fn main() {
    let counts = [0, 1, 2, 3, 4, 5];

    for count in counts {
        println!("count : {}", count);
    }
}

▶ 출력 결과

count : 0
count : 1
count : 2
count : 3
count : 4
count : 5
반응형
반응형

■ 조건문

조건의 상태가 참인지 거짓인지에 따라 분기에 실행 흐름을 제어할 수 있는 작성 방식입니다.


◆ IF 표현식

if 표현식은 제어하고자 하는 조건의 수에 따라 단일 if문, if~else문, if~else if~ else문으로 작성 할 수 있습니다.

1. 단일 if 문

number 변수는 10을 갖고, if 조건은 number가 10과 같은지를 확인합니다.

참(true)일 경우 if 분기를 처리하며, "Number 는 10 이다." 출력문이 호출됩니다.

거짓(false)일 경우 if 분기를 처리하지 않고 "조건문 테스트 종료." 출력문이 호출됨과 동시에 종료됩니다.

fn main() {
   let number = 10;

   if number == 10{
       println!("Number 는 10 이다.");
   }
   println!("조건문 테스트 종료.");
}

▶ 출력 결과(참[number == 10] 일 경우)

Number 는 10 이다.
조건문 테스트 종료.

▶ 출력 결과(거짓[number != 10] 일 경우)

조건문 테스트 종료.

2. if ~ else 문

number 변수는 10을 갖고, if 조건은 number가 10과 같은지를 확인합니다.

참(true)일 경우 if 분기를 처리하고 거짓(false)일 경우 else 분기를 처리한다.

fn main() {
   let number = 10;

   if number == 10{
       println!("Number 는 10 이다.");
   } else {
       println!("Number 는 10이 아니다.");
   }
}

▶ 출력 결과(참[number == 10]일 경우)

Number 는 10 이다.

▶ 출력 결과(거짓[number != 10]일 경우)

Number 는 10이 아니다.

3. if ~ else if ~ else 문

if ~ else if ~ else문은 if 문부터 순서대로 조건을 확인하다가 조건이 참인 분기만 처리하고 종료됩니다. 따라서 분기 처리 이후 조건중에 참인 경우가 있어도 작업은 수행하지 않습니다.

 

number 변수는 15를 갖는다. 15는 3의 배수이면서, 5의 배수도 될 수 있기 때문에 2번째 조건과, 3번째 조건 두 조건에 부합하지만 조건처리는 순서대로 진행됨에 따라 2번째 조건 로직만 수행되고 종료됩니다.

fn main() {
   let number = 15;

   if number % 2 == 0{
       // 거짓
       println!("Number 는 2의 배수이다.");
   } else if number % 3 == 0{
       // 참
       println!("Number 는 3의 배수이다.");
   } else if number % 5 == 0{
       // 참이지만 위 분기에서 종료
       println!("number 는 5의 배수이다.");
   } else {
       println!("number 는 2,3,5의 배수가 아니다.");
   }
}

▶ 출력 결과

Number 는 3의 배수이다.
반응형

'Programming Language > Rust' 카테고리의 다른 글

[Rust/러스트] 소유권(Owner)  (0) 2022.02.17
[Rust/러스트] 반복문 정리  (0) 2022.02.16
[Rust/러스트] 함수 정리  (0) 2022.02.15
[Rust/러스트] 배열 다루기  (0) 2022.02.11
[Rust/러스트] 튜플 다루기  (0) 2022.02.11
반응형

■ 함수

Rust에서의 함수는 표현부에 작성되어 있는 작업이 모두 수행되고 종료됩니다. 명명규칙은 스네이크 표기법을 따르며, 소문자와 언더바(_)를 사용하여 함수명을 표기합니다. 또한 함수는 매개변수와 반환값의 유무에 따라 각각 다른 기능으로 활용될 수 있습니다.


◆ 매개변수와 반환값이 없는 함수

fn basic_function(){
    println!("매개변수와 리턴값이 없는 함수");
}

fn main() {
    basic_function();
}

▶ 출력 결과

매개변수와 리턴값이 없는 함수

◆ 매개변수는 있고 반환값이 없는 함수

fn basic_function(x : i32, y :i32){
    println!("x : {}",x);
    println!("y : {}",y);
}

fn main() {
    basic_function(10, 20);
}

▶ 출력 결과

x : 10
y : 20

◆ 반환값은 있고 매개변수가 없는 함수

x 의 값은 표현식을 통해 값을 설정하고 있으며, 10을 갖는 y 와 1을 더한 값이 됩니다.

여기서 주의해야할 부분은 반환부입니다. 러스트에서 표현식의 반환부는 반환하고자 하는 데이터에 세미클론(;)을 붙이지 않음으로써 반환부로 인식합니다.

fn basic_function() -> i32{
    let x = {
        let y = 10;
        y + 1
    };
    x
}

fn main() {
    let result = basic_function();
    println!("result : {}", result);
}

▶ 출력 결과

result : 11

◆ 매개변수와 반환값이 있는 함수

fn basic_function(x : i32, y : i32) -> i32{
    x + y
}

fn main() {
    let result = basic_function(10, 20);
    println!("result : {}", result);
}

▶ 출력 결과

result : 30
반응형

'Programming Language > Rust' 카테고리의 다른 글

[Rust/러스트] 반복문 정리  (0) 2022.02.16
[Rust/러스트] 조건문 정리  (0) 2022.02.16
[Rust/러스트] 배열 다루기  (0) 2022.02.11
[Rust/러스트] 튜플 다루기  (0) 2022.02.11
[Rust/러스트] 변수 정의  (0) 2022.02.10

+ Recent posts