Rust로 도메인 고유형을 만들 때의 비결
통신부 형식 만들기
주소록 형식을 예로 들다.
우선 코드는 아래와 같다.코드 전체는 참조GiitHub 웨어하우스.
#[derive(Debug, Clone)]
pub struct AddressBook {
name: String,
entries: Vec<AddressEntry>,
}
impl Default for AddressBook {
fn default() -> Self {
Self {
name: String::default(),
entries: Vec::default(),
}
}
}
impl AddressBook {
pub fn new(name: &str) -> Self {
Self {
name: name.to_owned(),
entries: Vec::default(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn add_entry(&mut self, address_entry: AddressEntry) {
self.entries.push(address_entry);
}
pub fn remove_entry(&mut self, address_entry_id: AddressEntryId) -> AddressEntry {
let index = self
.entries
.iter()
.position(|e: &AddressEntry| e.id == address_entry_id)
.unwrap();
self.entries.remove(index)
}
pub fn iter(&self) -> impl Iterator<Item = &AddressEntry> {
self.entries.iter()
}
}
사용 방법은 다음과 같다.같은 변수 이름을 재사용했지만 Rust에서 컴파일 오류가 발생하지 않습니다.그림자let mut address_book = AddressBook::new("社員名簿");
let address_entry_id = AddressEntryId::new(1);
let personal_name = PersonName::new("Junichi", "Kato");
let address = Address::new("111-0001", "Tokyo-to", "minato-ku 1", Some("hoge 1 building"));
let address_entry = AddressEntry::new(address_entry_id, personal_name, address);
address_book.add_entry(address_entry);
let address_entry_id = AddressEntryId::new(2);
let personal_name = PersonName::new("Taro", "Yamamoto");
let address = Address::new("111-0002", "Tokyo-to", "minato-ku 2", Some("hoge 2 building"));
let address_entry = AddressEntry::new(address_entry_id, personal_name, address);
address_book.add_entry(address_entry);
let address_entry_id = AddressEntryId::new(3);
let personal_name = PersonName::new("Hanako", "Yamada");
let address = Address::new("111-0003", "Tokyo-to", "minato-ku 3", Some("hoge 3 building"));
let address_entry = AddressEntry::new(address_entry_id, personal_name, address);
address_book.add_entry(address_entry);
println!("name = {}", address_book.name())
address_book.iter().for_each(|e| println!("{:?}", e));
/* 出力例
name = 社員名簿
AddressEntry { id: AddressEntryId(1), name: PersonName { first_name: "Junichi", last_name: "Kato" }, address: Address { postal_code: "111-0001", pref: "Tokyo-to", address: "minato-ku 1", building: Some("hoge 1 building") } }
AddressEntry { id: AddressEntryId(2), name: PersonName { first_name: "Taro", last_name: "Yamamoto" }, address: Address { postal_code: "111-0002", pref: "Tokyo-to", address: "minato-ku 2", building: Some("hoge 2 building") } }
AddressEntry { id: AddressEntryId(3), name: PersonName { first_name: "Hanako", last_name: "Yamada" }, address: Address { postal_code: "111-0003", pref: "Tokyo-to", address: "minato-ku 3", building: Some("hoge 3 building") } }
*/
구조 정의
클래스 정의의 느낌으로 구조체를 정의하자.구조체 내부의 정의는 속성의 구성원에 해당한다.
#[derive(Debug, Clone)]
pub struct AddressBook {
name: String,
entries: Vec<AddressEntry>,
}
구성원에게 이 주소록의 이름을 가지게 한다.&str
,String
도대체 어떤 사람인지 다음 기사를 읽어보세요.String
형.주소 정보를 저장하는
AddressEntry
유형을 따로 제작한다.의존형도 적당히 만든다.#[derive(Debug, Clone)]
pub struct AddressEntry {
pub id: AddressEntryId,
pub name: PersonName,
pub address: Address,
}
AddressBook
는 여러 개AddressEntry
를 보존하기 때문에 entries
를 Vec<AddressEntry>
로 정의했다.길이가 고정되면 배열이 가능하지만 고정되지 않으면 선택Vec
.플랜트(관련 함수)
인스턴스를 생성할 때는 다음과 같이 설명할 수 있습니다.그러나 멤버가 표시되어야 합니다.
let address_book1 = AddressBook {
name: "従業員名簿".to_owned(),
entries: Vec::default(),
};
개인 구성원일 때 구조기처럼 작용하는 관련 함수를 정의한다. pub fn new(name: &str) -> Self {
Self {
name: name.to_owned(),
entries: Vec::default(),
}
}
는 정태적인 방법과 유사한 것으로 볼 수 있다.let address_book1 = AddressBook::new("従業員名簿");
Rust는 동일한 함수 이름으로 여러 매개변수 모드를 정의할 수 없습니다(과부하할 수 없음).기본적으로 각각 다른 이름을 지어야 한다.추기
trait
를 사용하면 과부하가 가능합니다.뭐, 다상성을 추구하지 않는데도 trait
괜찮겠어기술량이 늘었기 때문에 좋고 나쁨이 있다.그렇다면 각자 알기 쉬운 이름을 지어주는 게 좋을 것 같아서...정의 방법
첫 번째 파라미터에서 수신기를 설명하는 방법을 정의합니다.
pub fn name(&self) -> &str {
&self.name
}
&self
와 같다고 생각하세요self: &AddressBook
(뜻이 같아도 쓰지 마세요self: &AddressBook
.이 방법은 되돌아갈 수 밖에 없기 때문에 다음과 같은 내용을 쓸 수 있다.
pub fn name(&self) -> String {
self.name.clone()
}
움직이는 더러운 코드를 쓸 때는 괜찮겠지.하지만 쓸모없는 자리가 생겼다.이 경우 name
의 참고로 되돌아갈 수 있으며 클론에서 복제되었는지 여부는 호출자에게 맡겨야 합니다.처리 가변성
self.name
불변 참조이므로 상태를 변경할 수 없습니다.상태&self
를 변경하려면 &mut self
에 요소를 추가할 수 있습니다.pub fn add_entry(&mut self, address_entry: AddressEntry) {
self.entries.push(address_entry);
}
"어, 변형 가능한가요? 이제 안 변하죠?"이렇게 생각하는 사람.같이 읽읍시다.
특정한 범위 내에서 특정한 데이터에 대해 하나의 가변적인 참고만 있을 수 있다.
또한 인용할 때 가변 인용을 사용할 수 없다.
Rust의 가변성은 안전하게 처리할 수 있다.
정의된 서명을 통해 알 수 있듯이 가변 참조가 아닌 경우에는 가변 작업을 할 수 없습니다.변경되지 않을 때 가변 작업을 하면 컴파일 오류가 발생합니다.
let mut address_book = AddressBook::new("社員名簿");
address_book.add_entry(address_entry); // &mut self
let address_book = AddressBook::new("社員名簿");
address_book.add_entry(address_entry); // &selfはコンパイルエラー。そんなメソッドはない。
자바 등 언어로 등급을 바꾸면 속성이 많은 반은 따로 빌딩을 만들지만 Rust는 같은 유형으로 변하지 않고 변할 수 있다.self.entries
를 매개변수로 하는 Rust이므로 가능합니다.한 번도 안 변할 수 있는데...
위치를 개의치 않는다면 아래와 같이 하십시오.
pub fn add_entry(self, address_entry: AddressEntry) -> Self {
let mut new_self = self;
new_self.entries.push(address_entry);
Self {
..new_self
}
}
수정된 새로운 실례를 생성할 수 있습니다.기본적으로 원래의 실례를 사용하지 않고 필요할 때self
만 있으면 되기 때문에 아니다clone
면 충분하다.let address_book = AddressBook::default();
let address_book = address_book.add_entry(address_entry);
// 更新前のインスタンスを残したいなら、呼び出し元でclone()すればいい
// let address_book_updated = address_book.clone().add_entry(address_entry);
다양한 설치를 읽었지만 이런 설치는 보기 드물다.기본적으로 불변 인용을 사용하면 부작용을 억제할 수 있지만 가변은 좁은 범위에서 사용하고 제어를 없애면 불변을 회복할 수 있다.Vec 대신 슬라이스를 매개변수로 사용
Rust에는 가변 길이 매개변수가 없습니다.동일한 유형의 매개변수를 여러 개 가져올 때는 Vec 또는 슬라이스입니다.다음 글을 읽으십시오.
&self
형은 괜찮은 것 같아요.pub fn add_entries(&mut self, address_entries: Vec<AddressEntry>) {
슬라이스에 대한 참조를 받는 것이 편리합니다.이번에는 요소형 실체self
가 Vec
형to_vec
형Vec
으로 바뀌어야 하기 때문에 위치가 발생한다.(이것이 성능에 문제가 있다면 into_iter
이동이 가능할 수도 있습니다...)pub fn add_entries(&mut self, address_entries: &[AddressEntry]) {
address_entries
.to_vec()
.into_iter()
.for_each(|e: AddressEntry| self.add_entry(e))
}
Vec 또는 슬라이드를 사용하여 값 집합을 추출할 수 있습니다.let mut address_book = AddressBook::new("社員名簿");
let entries = [address1.clone(), address2.clone()]; // 配列
address_book.add_entries(&entries); // スライスの参照
let entries = vec![address1, address2]; // Vec
address_book.add_entries(&entries); // Derefの実装がスライスの参照を返す
보충:깨우쳐 주셔서 감사합니다.
그래서 나는 즉각 실현해 보았다.
pub fn add_entries1(&mut self, address_entries: impl IntoIterator<Item=AddressEntry>) {
address_entries
.into_iter()
.for_each(|e| self.add_entry(e))
}
let entries1= [address_entry1.clone(), address_entry2.clone()];
address_book.add_entries1(entries1);
let entries2= vec![address_entry1, address_entry2];
address_book.add_entries1(entries2);
슬라이스 참조로 수신address_entries: Vec<AddressEntry>
하면 위치가 생기기 때문에 이쪽을 움직일 수 있어 효율이 높습니다.
Reference
이 문제에 관하여(Rust로 도메인 고유형을 만들 때의 비결), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/j5ik2o/articles/d37bd2c6924446텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)