수색…
소개
특성은 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 컴파일러는 Dog
및 Person
유형 모두에 대해 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; }
}
관련 유형 참조
타입 T
가 GetItems
를 제네릭처럼 구현한다고 확신 할 경우, 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);
}