rust 如何存储不同的数据以及它们之间的关系

fquxozlt  于 2023-03-08  发布在  其他
关注(0)|答案(1)|浏览(148)

我有一个书单和一个作者列表,其中一本书是由一个作者写的,而一个作者可能写了很多本书,我该如何在Rust程序中编码呢?
要求:
1.对于任何给定的对象,访问与之相关的对象应该是低复杂度的。
1.应该防止不可能的状态。如果数据是冗余的,则会出现不一致。
一种方法:
每个作者和书都由一个唯一的值标识(这里是表中的索引)。

struct Book {
    author: usize,
}

struct Author {
    books: Vec<usize>,
}

let books = vec![
    Book { author: 0 },
    Book { author: 0 },
    Book { author: 1 },
];

let authors = vec![
    Author { books: vec![0, 1] },
    Author { books: vec![2] },
];

这种方法满足了第一个要求,但不满足第二个要求。在一个实现中,可以使用散列Map或Slab集合来确保id保持有效。
我不知道如何使用Rust类型系统和不同的工具来建立满足我的两个需求的对象之间的关系。
这是编程中经常出现的一种模式,我可能是在重新发明轮子。

mqkwyuun

mqkwyuun1#

我不知道你需要多少帮助也许你会发现下面的例子是微不足道的。
资源(书籍和作者)的所有权完全在图书馆的控制之下。因为场景非常简单(不删除书籍和作者),这些资源之间的关系只依赖于数字索引。如果我们需要删除资源的能力,一些generational indices可以帮助我们;在这种情况下,当访问这些资源时,必须考虑一些可能的故障(Option/Result)。
在当前的场景中,所有关于索引的细节都隐藏在公共接口中。因此,我们提供迭代器(impl Iterator...)来检查资源。如果索引必须在公共接口中公开,则仍然可以通过迭代器进行随机访问(尽管提供切片可能会更自然)。
注意,由于impl,迭代器类型在编译步骤中是精确已知的,并且可能会被优化掉,就好像我们直接在主程序中处理带有数字索引的切片一样。

// edition = 2021
mod example {
    pub struct Book {
        title: String,
        author_id: usize,
    }
    impl Book {
        pub fn title(&self) -> &str {
            &self.title
        }
    }

    pub struct Author {
        name: String,
        book_ids: Vec<usize>,
    }
    impl Author {
        pub fn name(&self) -> &str {
            &self.name
        }
    }

    pub struct Library {
        books: Vec<Book>,
        authors: Vec<Author>,
    }
    impl Library {
        pub fn new() -> Self {
            Self {
                books: Vec::new(),
                authors: Vec::new(),
            }
        }

        pub fn register_book(
            &mut self,
            book_title: String,
            author_name: String,
        ) {
            let author_id = if let Some(pos) =
                self.authors.iter().position(|x| x.name == author_name)
            {
                pos
            } else {
                self.authors.push(Author {
                    name: author_name,
                    book_ids: Vec::new(),
                });
                self.authors.len() - 1
            };
            self.authors[author_id].book_ids.push(self.books.len());
            self.books.push(Book {
                title: book_title,
                author_id,
            })
        }

        pub fn books(&self) -> impl Iterator<Item = &Book> {
            self.books.iter()
        }

        pub fn authors(&self) -> impl Iterator<Item = &Author> {
            self.authors.iter()
        }

        pub fn author_of(
            &self,
            book: &Book,
        ) -> &Author {
            &self.authors[book.author_id]
        }

        pub fn books_by<'a>(
            &'a self,
            author: &'a Author,
        ) -> impl Iterator<Item = &Book> + 'a {
            author.book_ids.iter().map(|id| &self.books[*id])
        }
    }
}

fn main() {
    let mut library = example::Library::new();
    library.register_book("title A".to_owned(), "name X".to_owned());
    library.register_book("title B".to_owned(), "name X".to_owned());
    library.register_book("title C".to_owned(), "name Y".to_owned());
    println!("~~ inspect by authors ~~");
    for author in library.authors() {
        let titles = library
            .books_by(author)
            .map(|b| format!("{:?}", b.title()))
            .collect::<Vec<_>>();
        println!("author {:?} wrote {}", author.name(), titles.join(", "));
    }
    println!("~~ inspect by books ~~");
    for book in library.books() {
        println!(
            "book {:?} written by {:?}",
            book.title(),
            library.author_of(book).name()
        );
    }
    println!("~~ random access ~~");
    for idx in [2, 3] {
        if let Some(book) = library.books().nth(idx) {
            println!("book {} is {:?}", idx, book.title());
        } else {
            println!("book {} does not exist", idx);
        }
    }
}
/*
~~ inspect by authors ~~
author "name X" wrote "title A", "title B"
author "name Y" wrote "title C"
~~ inspect by books ~~
book "title A" written by "name X"
book "title B" written by "name X"
book "title C" written by "name Y"
~~ random access ~~
book 2 is "title C"
book 3 does not exist
*/

相关问题