수색…


소개

특성은 struct 가 구현해야하는 '계약'을 설명하는 방법입니다. 특성은 일반적으로 메서드 서명을 정의하지만 특성 경계를 허용하면 특성 의 다른 메서드를 기반으로 구현을 제공 할 수도 있습니다.

객체 지향 프로그래밍에 익숙한 사람들에게는 특성이 약간의 차이가있는 인터페이스로 간주 될 수 있습니다.

통사론

  • 형질 특성 {fn method (...) -> ReturnType; ...}
  • 형질 특성 : 묶인 {fn 메서드 (...) -> ReturnType; ...}
  • impl 형의 형질 {fn method (...) -> ReturnType {...} ...}
  • impl <T> Trait for T 여기서 T : 경계 {fn method (...) -> ReturnType {...} ...}

비고

  • 형질은 일반적으로 인터페이스와 비슷하지만 둘 사이를 구별하는 것이 중요합니다. Java와 같은 OO 언어에서 인터페이스는 인터페이스를 확장하는 클래스의 필수적인 부분입니다. Rust에서 컴파일러는 구조체의 특성을 사용하지 않는 한 구조체의 특성을 전혀 모릅니다.

기초

특성 만들기

trait Speak {
    fn speak(&self) -> String;
}

특성 구현

struct Person;
struct Dog;

impl Speak for Person {
    fn speak(&self) -> String {
        String::from("Hello.")
    }
}

impl Speak for Dog {
    fn speak(&self) -> String {
        String::from("Woof.")
    }
}

fn main() {
    let person = Person {};
    let dog = Dog {};
    println!("The person says {}", person.speak());
    println!("The dog says {}", dog.speak());
}

정적 및 동적 디스패치

특정 형질을 구현하는 객체를 받아들이는 함수를 생성하는 것이 가능합니다.

정적 디스패치

fn generic_speak<T: Speak>(speaker: &T) {
    println!("{0}", speaker.speak());
}

fn main() {
    let person = Person {};
    let dog = Dog {};

    generic_speak(&person);
    generic_speak(&dog);
}

Static 디스패치는 여기에서 사용됩니다. 즉, Rust 컴파일러는 DogPerson 유형 모두에 대해 generic_speak 함수의 특수 버전을 생성합니다. 컴파일하는 동안이 다형 함수 (또는 임의의 다형성 엔티티)의 특수화 된 버전을 단일 변형 이라고 합니다 .

다이나믹 디스패치

fn generic_speak(speaker: &Speak) {
    println!("{0}", speaker.speak());
}

fn main() {
    let person = Person {};
    let dog = Dog {};

    generic_speak(&person as &Speak);
    generic_speak(&dog); // gets automatically coerced to &Speak
}

컴파일 된 바이너리에는 generic_speak 의 단일 버전 만 존재하며 런타임에 vtable 조회를 사용하여 speak() 호출이 수행됩니다. 따라서 동적 디스패치를 ​​사용하면 컴파일 시간이 단축되고 컴파일 된 바이너리 크기가 작아지고 런타임은 약간 느려집니다.

&Speak 또는 Box<Speak> 유형의 객체를 특성 객체 라고 합니다 .

관련 유형

  • 특성을 구현하는 유형과 연관된 유형간에 일대일 관계가있는 경우 연관된 유형을 사용하십시오.
  • 이것은 때로는 출력 유형 이라고도하며, 특성을 적용 할 때 유형에 주어진 항목이기 때문에 출력 유형 이라고도합니다.

창조

trait GetItems {
    type First;
//  ^~~~ defines an associated type. 
    type Last: ?Sized;
//           ^~~~~~~~ associated types may be constrained by traits as well
    fn first_item(&self) -> &Self::First;
//                           ^~~~~~~~~~~ use `Self::` to refer to the associated type 
    fn last_item(&self) -> &Self::Last;
//                          ^~~~~~~~~~ associated types can be used as function output...
    fn set_first_item(&mut self, item: Self::First);
//                                     ^~~~~~~~~~~  ... input, and anywhere.
}

구현

impl<T, U: ?Sized> GetItems for (T, U) {
    type First = T;
    type Last = U;
//              ^~~ assign the associated types
    fn first_item(&self) -> &Self::First { &self.0 }
    fn last_item(&self) -> &Self::Last { &self.1 }
    fn set_first_item(&mut self, item: Self::First) { self.0 = item; }
}

impl<T> GetItems for [T; 3] {
    type First = T;
    type Last = T;
    fn first_item(&self) -> &T { &self[0] }
//                           ^ you could refer to the actual type instead of `Self::First`
    fn last_item(&self) -> &T { &self[2] }
    fn set_first_item(&mut self, item: T) { self[0] = item; }
}

관련 유형 참조

타입 TGetItems 를 제네릭처럼 구현한다고 확신 할 경우, T::First 를 사용하여 연관된 타입을 얻을 수 있습니다.

fn get_first_and_last<T: GetItems>(obj: &T) -> (&T::First, &T::Last) {
//                                               ^~~~~~~~ refer to an associated type
    (obj.first_item(), obj.last_item())
}

그렇지 않으면 유형을 구현하는 특성을 컴파일러에 명시 적으로 알려야합니다

let array: [u32; 3] = [1, 2, 3];
let first: &<[u32; 3] as GetItems>::First = array.first_item();
//          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [u32; 3] may implement multiple traits which many
//                                        of them provide the `First` associated type.
//                                        thus the explicit "cast" is necessary here.
assert_eq!(*first, 1);

관련 유형으로 제한

fn clone_first_and_last<T: GetItems>(obj: &T) -> (T::First, T::Last)
    where T::First: Clone, T::Last: Clone
//  ^~~~~ use the `where` clause to constraint associated types by traits
{
    (obj.first_item().clone(), obj.last_item().clone())
}

fn get_first_u32<T: GetItems<First=u32>>(obj: &T) -> u32 {
//                          ^~~~~~~~~~~ constraint associated types by equality
    *obj.first_item()
}

기본 메소드

trait Speak {
    fn speak(&self) -> String {
        String::from("Hi.")
    }
}

이 메소드는 impl 블록에서 덮어 쓰기되는 경우를 제외하고 기본적으로 호출됩니다.

struct Human;
struct Cat;

impl Speak for Human {}

impl Speak for Cat {
    fn speak(&self) -> String {
        String::from("Meow.")
    }
}

fn main() {
    let human = Human {};
    let cat = Cat {};
    println!("The human says {}", human.speak());
    println!("The cat says {}", cat.speak());
}

출력 :

인간이 안녕하세요.

고양이는 야옹을 말한다.

특성에 경계를 두는 것

새로운 특성을 정의 할 때이 특성을 구현하고자하는 유형이 많은 제약 사항 또는 경계를 검증하도록 할 수 있습니다.

표준 라이브러리에서 예제를 취한 DerefMut 특성은 형제가 형제 Deref 특성을 먼저 구현해야합니다.

pub trait DerefMut: Deref {
    fn deref_mut(&mut self) -> &mut Self::Target;
}

이는 다시 수 DerefMut 관련 타입 사용하는 Target 에 의해 정의 Deref .


신택스가 상속을 연상케하는 동안 :

  • 바인딩 된 특성의 모든 관련 항목 (상수, 유형, 함수 등)을 가져옵니다.
  • 그것은에서 다형성을 가능하게 &DerefMut 하는 &Deref

이것은 사실상 다릅니다.

  • 평생 (예 : 'static )을 경계로 사용할 수 있습니다
  • 바인딩 된 특성 항목을 무시할 수는 없습니다 (함수조차도 제외)

따라서 그것을 별도의 개념으로 생각하는 것이 가장 좋습니다.

다중 바인드 객체 유형

Static Dispatch 함수에 여러 객체 유형을 추가 할 수도 있습니다.

fn mammal_speak<T: Person + Dog>(mammal: &T) {
    println!("{0}", mammal.speak());
}

fn main() {
    let person = Person {};
    let dog = Dog {};

    mammal_speak(&person);
    mammal_speak(&dog);
}


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow