녹 제네릭

나중에 지정되는 유형에 사용할 수 있는 방식으로 일반화된 알고리즘을 작성하는 방법입니다. 매개변수의 도움으로 지정된 유형에 대해 초기화할 수 있습니다.

우리가 제네릭을 사용하는 것:-



  • 추상 유형: i32 또는 float와 같은 명시적 유형을 작성하는 대신 다른 코드를 작성하여 다른 유형에 대한 제네릭을 사용할 수 있습니다
  • .

  • 유연성 추가: 동일한 코드로 다양한 데이터 유형을 사용할 수 있습니다.

  • 코드 중복 감소: 두 가지 유형에 대해 동일한 코드 또는 둘 다를 가질 수 있습니다.

  • 런타임 비용 없음: 개체 특성과 달리 제네릭에는 런타임 비용이 없습니다. 컴파일러는 컴파일 시간 동안 백그라운드에서 다양한 유형을 생성합니다.



  • 시작하자



    일반적으로 제네릭 유형 매개변수는 <T> 로 표시됩니다. 그러나 당신은 무엇이든 자유롭게 사용할 수 있습니다.

    구조체



    1. 단일 자리 표시자 유형



    자리 표시자/제네릭 유형으로 구조체를 정의합니다.

    struct Point<T> {
        x: T,
        y: T,
    }
    


    이것을 i32에 사용해 봅시다.

    pub fn main() {
        let i32_point = Point { x: 45, y: 90 };
        println!("x is {} and y is {}", i32_point.x, i32_point.y);
    }
    


    i32_point를 마우스로 가리키면 변수 유형이 Point i32 i32임을 알 수 있습니다. 그러나 제한되지 않습니다. x와 y 모두에 부동 소수점을 사용할 수도 있지만 동일해야 합니다.

    그렇다면 두 필드 x와 y에 대해 서로 다른 유형을 어떻게 사용할 수 있습니까?

    두 자리 표시자를 사용하여 수행할 수 있습니다.

    2. 두 자리 표시자



    구조체 정의

    struct TwoPoint<T, U> {
        x: T,
        y: U,
    }
    


    테스트하자

    pub fn main() {
        let explicit_point = TwoPoint { x: 9, y: 90.3 };
        println!(
            "explicit_point x is {} and y is {}",
            explicit_point.x, explicit_point.y
        );
    }
    


    유사하게 우리는 x와 y, 심지어 벡터에 대해 두 가지 다른 데이터 유형을 사용할 수 있습니다.

    열거형



    1. 열거형 제네릭



    통과, 실패 또는 대기 여부에 관계없이 결과를 저장할 결과 열거형을 만듭니다.

    #[derive(Debug)]
    enum Result<T> {
        Passed(T),
        Failed(T),
        Pending,
    }
    


    모든 프리미티브에 디버그 기능/특성이 포함되어 있기 때문에 열거형이 프리미티브가 아니므로 Debug 특성을 사용했습니다.

    테스트

    pub fn main() {
      let result = Result::Passed(43);
      match result {
          Result::Passed(a) => println!("Student Passed with {}", a),
          Result::Failed(b) => println!("Student Failed with {}", b),
          Result::Pending => println!("Student result is pending"),
      }
    }
    


    위의 경우와 같이 Passed 및 Failed에 대해 서로 다른 제네릭 유형을 사용할 수 있습니다.

    2. 제네릭과 함께 구조체 및 열거형 사용



    우리는 위의 예에서 Result 열거형을 사용하고 있습니다.

    struct Student<T> {
        name: String,
        roll_no: T,
        result: Result<T>,
    }
    


    그것을 사용하자

    pub fn main() {
      let student = Student {
          name: "Praveen".to_string(),
          roll_no: 78,
          result: Result::Passed(98),
      };
    
      match student.result {
          Result::Passed(a) => println!("Student Passed with {}", a),
          Result::Failed(b) => println!("Student Failed with {}", b),
          Result::Pending => println!("Student result is pending"),
      }
    }
    


    제네릭이 있는 함수



    1. 단일 일반 유형 매개변수와 단일 특성을 가진 함수



    기능 정의

    fn custom_add<T: std::ops::Add<Output = T>>(a: T, b: T) -> T {
        a + b
    }
    


    여기서 Output=T는 + 또는 - 결과가 동일해야 함을 보장합니다. 두 개의 원자를 추가하면 분자(T+T=U)가 되는 것처럼 원하는 경우 다를 수 있습니다.

    let a = 4;
    let b = 5;
    println!("sum of {} and {} is {}", a, b, custom_add(a, b));
    


    2. 단일 일반 유형 매개변수와 여러 특성을 가진 함수




    fn custom_add_sub<T: std::ops::Add<Output = T> + std::ops::Sub<Output = T> + std::fmt::Debug>(
        a: T,
        b: T,
    ) -> T {
        // Debug trait is needed for this print statement
        println!("{:?} {:?}", a, b);
        a + b
    }
    


    유형 사용

    pub fn main() {
      println!("sub of {} and {} is {}", a, b, custom_add_sub(a, b));
    }
    


    나는 지금 당신이 쉽다는 것을 압니다.

    3.여러 일반 유형 매개변수와 여러 특성 및 where 키워드가 포함된 함수




    // using the where to define a generic type
    fn custom_add_sub_mul<T, E>(a: T, b: T, c: E) -> T
    where
        T: std::ops::Mul<Output = T> + std::ops::Add<Output = T> + std::ops::Sub<Output = T>,
        E: std::fmt::Debug,
    {
        println!("{:?}", c);
        a * b
    }
    

    where 키워드는 코드를 더 명확하게 만듭니다. 코드 실행 방식에는 영향을 미치지 않습니다.

    pub fn main() {
      println!("mul of {} and {} is {}", a, b, custom_add_sub_mul(a, b, b));
    }
    


    4. 커스텀 트레이트가 있는 기능



    미리 정의된 특성을 사용하는 대신 원하는 대로 사용자 지정 특성을 사용할 수 있습니다.

    trait SomeCustomTrait {
        fn bla_bla(&self, a: &str, b: &str) -> String;
    }
    #[derive(Debug)]
    struct TestStruct {
        something: i32,
    }
    
    impl SomeCustomTrait for TestStruct {
        fn bla_bla(&self, a: &str, b: &str) -> String {
            self.something.to_string() + "-" + a + "-" + b
        }
    }
    
    fn do_this<T>(some_var: &T) -> String
    where
        T: SomeCustomTrait + std::fmt::Debug,
    {
        println!("{:?}", some_var);
        some_var.bla_bla("first", "second")
    }
    


    함수에서 우리는 디버그와 함께 우리의 SomeCustomTrait를 사용했습니다. 더 추가할 수도 있습니다.

    pub fn main() {
      let test = TestStruct { something: 1000 };
      let result = do_this(&test);
    }
    


    제네릭 구현




    struct ImplStruct<T, U> {
        field1: T,
        field2: U,
    }
    
    impl<T, U> ImplStruct<T, U>
    where
        T: std::fmt::Debug,
        U: std::fmt::Debug,
    {
        fn log_something(&self) {
            println!("{:?} {:?}", self.field1, self.field2);
        }
    }
    


    여기에서 제네릭 유형이 구조체 대신 impl block에 정의되어 있음을 알 수 있지만 두 블록 모두에서 정의할 수 있지만 Impl 블록은 필수입니다.

    pub fn main() {
      let impl_test = ImplStruct{
          field1:5.6,
          field2:vec![1,2,3],
      };
      impl_test.log_something();
    


    그래서 우리는 Rust Generics의 기초를 다루었지만 Generics로 더 많은 것을 할 수 있습니다.

    자원
  • rust-lang by example


  • 소스 코드
    RustyRustLessons Generics

  • 해피해킹
    러스타시안!

    좋은 웹페이지 즐겨찾기