데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      Openlab - 5차 Rust 스터디

      lambda 24.06.11
      99 1 0
      DEVOTEE 요약
      5주차 스터디에서는 러스트의 제네릭 타입, 트레이트, 라이프타임에 대해 다루며 심도 있는 논의를 했습니다. 9장에서는 에러 처리 방법으로 panic!과 Result 타입을 다루었고, 10장에서는 제네릭 타입과 트레이트, 라이프타임의 정의와 사용법을 설명했습니다. 스터디 진행이 점점 어려워지지만 모든 사람들이 중도 포기를 하지 않기를 당부했습니다.
      DEVOTEE 추천 블로그

      5주차 스터디 소개

      5주차 스터디는 6월 10일에 진행 했습니다. 러스트의 가장 어렵고, 핵심적인 내용인, "10장 제네릭 타입, 트레이트, 라이프타임" 에 대한 내용에 대해 공유 했고, 늘상 스터디를 진행하면서, 모이신 분들의 여러 경험과 다른 언어에 대한 이야기를 하면서 이해도를 높였는데 특히 이번 스터디를 진행 할 때 많은 심도 있는 이야기를 나눴던 것 같습니다. 스터디 진행의 중반 정도 달려온 것 같습니다. 개인 사정으로 빠지신 분들도 계시고 내용도 점점 어려워 지지만 모두 "중꺽마"를 잊지 않기를 당부 했습니다.

      이번 스터디는 책는 "9장 예외처리", "10장 제네릭 타입, 트레이트, 라이프타임"에 대해 저와, 김유진님이 정리하여 발표해 주셨습니다.




      9장, 10장에 대한 간략한 내용 정리.

      9. 에러 처리

      러스트에서는 안전하고 견고한 프로그램을 작성하기 위해 에러 처리를 중요하게 다룹니다. 러스트의 에러 처리 방식은 크게 두 가지로 나눌 수 있습니다: 복구 불가능한 에러와 복구 가능한 에러. 이번 글에서는 panic! 매크로를 사용한 복구 불가능한 에러 처리와 Result 타입을 사용한 복구 가능한 에러 처리 방법, 그리고 이 둘을 언제 사용해야 할지에 대해 알아보겠습니다.

      9.1. panic!으로 복구 불가능한 에러 처리하기

      panic! 매크로는 프로그램이 실행되는 동안 복구 불가능한 에러가 발생했을 때 호출됩니다. 이 매크로는 프로그램을 즉시 중단하고 스택을 언와인딩(unwinding)하여 모든 정리 작업을 수행합니다.

      panic! 사용 예제

      fn main() {
          panic!("Something went wrong!");
      }

      위 코드가 실행되면 프로그램은 즉시 종료되고 다음과 같은 메시지를 출력합니다:

      thread 'main' panicked at 'Something went wrong!', src/main.rs:2:5

      이 메시지는 panic!이 호출된 파일과 위치를 나타냅니다.

      스택 언와인딩과 어보트

      러스트는 기본적으로 스택 언와인딩을 수행하여 패닉이 발생했을 때 모든 정리 작업을 합니다. 하지만, Cargo.toml 파일에서 panic = 'abort'로 설정하면 언와인딩 대신 즉시 중단(abort)시킬 수 있습니다. 이렇게 하면 프로그램이 중단되지만 정리 작업은 수행되지 않습니다.

      
      [profile.release]
      panic = 'abort'
      

      9.2. Result로 복구 가능한 에러 처리하기

      Result 타입은 일반적으로 함수가 성공적으로 완료되었는지 또는 에러가 발생했는지를 나타내기 위해 사용됩니다. Result 타입은 두 가지 변형을 가집니다: Ok와 Err. Ok는 작업이 성공했음을 나타내고, Err는 작업이 실패했음을 나타냅니다.

      enum Result<T, E> {
          Ok(T),
          Err(E),
      }

      Result 사용 예제

      파일을 읽는 예제를 통해 Result 타입을 사용하는 방법을 알아보겠습니다.

      use std::fs::File;
      use std::io::ErrorKind;
      
      fn main() {
          let file = File::open("hello.txt");
      
          let file = match file {
              Ok(file) => file,
              Err(ref error) if error.kind() == ErrorKind::NotFound => {
                  match File::create("hello.txt") {
                      Ok(fc) => fc,
                      Err(e) => panic!("Problem creating the file: {:?}", e),
                  }
              }
              Err(error) => {
                  panic!("Problem opening the file: {:?}", error);
              }
          };
      }

      위 예제에서 File::open 함수는 Result 타입을 반환합니다. match 표현식을 사용하여 파일이 성공적으로 열렸는지, 파일이 없어서 새로 생성해야 하는지, 다른 종류의 에러가 발생했는지를 처리합니다.

      에러 전파

      러스트에서는 에러를 호출자에게 전파할 수 있습니다. 이를 위해 ? 연산자를 사용할 수 있습니다.

      use std::fs::File;
      use std::io::{self, Read};
      
      fn read_username_from_file() -> Result<String, io::Error> {
          let mut file = File::open("hello.txt")?;
          let mut username = String::new();
          file.read_to_string(&mut username)?;
          Ok(username)
      }

      ? 연산자는 에러가 발생하면 해당 에러를 즉시 반환하고, 그렇지 않으면 다음 줄로 넘어갑니다. 이를 통해 에러 처리를 간결하게 할 수 있습니다.

      9.3. panic!이냐, panic!이 아니냐, 그것이 문제로다

      panic!과 Result 중 어느 것을 사용할지는 상황에 따라 다릅니다. 일반적으로 panic!은 복구 불가능한 에러에 사용하고, Result는 복구 가능한 에러에 사용합니다.

      언제 panic!을 사용할까?

      1. 프로그램이 잘못된 상태에 있을 때: 예를 들어, 배열의 인덱스가 범위를 벗어나는 경우.

      2. 복구할 방법이 없을 때: 예를 들어, 심각한 논리 오류가 발생한 경우.

      언제 Result를 사용할까?

      1. 에러가 발생할 수 있는 정상적인 상황: 예를 들어, 파일을 열거나 네트워크 요청을 보낼 때.

      2. 호출자에게 에러를 처리할 기회를 주고 싶을 때: 에러를 호출자가 처리하도록 하고 싶을 때.

      10. 제네릭 타입, 트레이트, 라이프타임

      러스트에서 제네릭 타입, 트레이트, 라이프타임은 코드의 유연성과 재사용성을 높이는 데 중요한 역할을 합니다. 이 글에서는 제네릭 데이터 타입, 트레이트를 사용하여 공통된 동작을 정의하는 방법, 그리고 라이프타임을 통해 참조자의 유효성을 검증하는 방법에 대해 자세히 알아보겠습니다.

      10.1. 제네릭 데이터 타입

      제네릭 데이터 타입을 사용하면 특정 타입에 구애받지 않는 유연한 함수를 작성할 수 있습니다. 제네릭은 코드 중복을 줄이고, 다양한 타입에 대해 동작할 수 있는 함수를 작성하는 데 유용합니다.

      제네릭 타입 정의

      함수 정의에 제네릭 타입을 사용하는 예제입니다.

      fn largest<T: PartialOrd>(list: &[T]) -> &T {
          let mut largest = &list[0];
          for item in list {
              if item > largest {
                  largest = item;
              }
          }
          largest
      }

      위 예제에서 largest 함수는 PartialOrd 트레이트를 구현한 임의의 타입 T에 대해 동작할 수 있습니다. list의 요소를 순회하며 가장 큰 값을 찾습니다.

      구조체에 제네릭 타입 사용

      구조체 정의에 제네릭 타입을 사용할 수 있습니다.

      struct Point<T> {
          x: T,
          y: T,
      }
      
      let integer = Point { x: 5, y: 10 };
      let float = Point { x: 1.0, y: 4.0 };

      위 예제에서 Point 구조체는 제네릭 타입 T를 사용하여 정의되었습니다. 이를 통해 Point 구조체는 i32, f64 등 다양한 타입으로 인스턴스화할 수 있습니다.

      열거형에 제네릭 타입 사용

      열거형 정의에 제네릭 타입을 사용할 수 있습니다.

      enum Option<T> {
          Some(T),
          None,
      }
      
      enum Result<T, E> {
          Ok(T),
          Err(E),
      }

      위 예제에서 Option과 Result 열거형은 제네릭 타입을 사용하여 정의되었습니다. 이를 통해 다양한 타입의 값을 포장할 수 있습니다.

      10.2. 트레이트로 공통된 동작을 정의하기

      트레이트(Trait)는 공통된 동작을 정의하는 데 사용됩니다. 트레이트는 다른 타입이 특정 동작을 구현하도록 강제할 수 있습니다.

      트레이트 정의 및 구현

      먼저, 트레이트를 정의하고 이를 구현하는 방법을 살펴보겠습니다.

      trait Summary {
          fn summarize(&self) -> String;
      }
      
      struct Article {
          headline: String,
          content: String,
      }
      
      impl Summary for Article {
          fn summarize(&self) -> String {
              format!("{}: {}", self.headline, self.content)
          }
      }
      
      let article = Article {
          headline: String::from("Rust 1.54 Released"),
          content: String::from("Rust 1.54 is now available. It includes..."),
      };
      
      println!("New article available! {}", article.summarize());

      위 예제에서 Summary 트레이트는 summarize 메서드를 정의합니다. Article 구조체는 Summary 트레이트를 구현하여 summarize 메서드를 제공합니다.

      트레이트 바운드

      트레이트 바운드는 제네릭 타입이 특정 트레이트를 구현하도록 강제할 수 있습니다.

      fn notify<T: Summary>(item: &T) {
          println!("Breaking news! {}", item.summarize());
      }

      위 예제에서 notify 함수는 제네릭 타입 T에 대해 동작하며, T는 Summary 트레이트를 구현해야 합니다.

      여러 트레이트 바운드

      여러 트레이트 바운드를 사용하는 방법도 있습니다.

      fn notify<T: Summary + Display>(item: &T) {
          println!("Breaking news! {}", item.summarize());
      }

      위 예제에서 notify 함수는 Summary와 Display 트레이트를 모두 구현한 타입에 대해 동작합니다.

      트레이트 객체

      트레이트 객체를 사용하면 동적 디스패치를 통해 다양한 타입을 처리할 수 있습니다.

      fn notify(item: &dyn Summary) {
          println!("Breaking news! {}", item.summarize());
      }

      위 예제에서 notify 함수는 Summary 트레이트를 구현한 임의의 타입에 대해 동작할 수 있습니다.

      10.3. 라이프타임으로 참조자의 유효성 검증하기

      러스트의 라이프타임은 참조자가 유효한 범위를 지정합니다. 라이프타임을 통해 컴파일러는 메모리 안전성을 보장할 수 있습니다.

      기본 라이프타임 규칙

      러스트 컴파일러는 대부분의 경우 라이프타임을 자동으로 추론할 수 있습니다. 하지만 복잡한 경우에는 명시적으로 라이프타임을 지정해야 합니다.

      fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
          if x.len() > y.len() {
              x
          } else {
              y
          }
      }

      위 예제에서 longest 함수는 두 개의 문자열 슬라이스를 받아 더 긴 문자열 슬라이스를 반환합니다. 이 함수의 모든 참조자는 동일한 라이프타임 'a를 가집니다.

      구조체에 라이프타임 사용

      구조체 정의에 라이프타임을 사용할 수 있습니다.

      struct ImportantExcerpt<'a> {
          part: &'a str,
      }
      
      let novel = String::from("Call me Ishmael. Some years ago...");
      let first_sentence = novel.split('.').next().expect("Could not find a '.'");
      let i = ImportantExcerpt {
          part: first_sentence,
      };

      위 예제에서 ImportantExcerpt 구조체는 문자열 슬라이스를 참조하며, 해당 슬라이스의 라이프타임 'a를 지정합니다.

      라이프타임 엘리시온

      러스트 컴파일러는 함수 시그니처에서 라이프타임을 생략할 수 있는 경우를 처리합니다. 이를 라이프타임 엘리시온(elision)이라고 합니다.

      fn first_word(s: &str) -> &str {
          &s[..]
      }

      위 예제에서 first_word 함수의 입력과 출력 참조자는 동일한 라이프타임을 가집니다. 컴파일러는 이를 추론하여 라이프타임을 명시적으로 지정하지 않아도 됩니다.

      결론

      러스트에서는 에러 처리와 코드 재사용성을 높이기 위해 다양한 강력한 기능을 제공합니다. panic!과 Result를 사용하여 에러를 처리할 수 있으며, panic!은 복구 불가능한 에러에, Result는 복구 가능한 에러에 사용합니다. 이를 통해 안전하고 견고한 프로그램을 작성할 수 있습니다. 상황에 맞게 적절한 에러 처리 방법을 선택하여 프로그램의 안정성과 가독성을 높일 수 있습니다.

      또한, 제네릭 타입, 트레이트, 라이프타임은 러스트의 코드 유연성과 재사용성을 높이는 데 중요한 역할을 합니다. 제네릭 타입을 사용하면 특정 타입에 구애받지 않는 유연한 코드를 작성할 수 있으며, 트레이트를 사용하면 공통된 동작을 정의하고 강제할 수 있습니다. 라이프타임을 통해 참조자의 유효성을 검증하여 메모리 안전성을 보장할 수 있습니다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      lambda 님의 최신 블로그

      더보기

      DEVOTEE 추천 블로그

      동영상 기고하기