速成课第2节-练习解答

下面是上一节 Rust 速成课 “所有权基础” 练习解答。

这篇文章是基于 FP 完成 Rust 教学系列的一部分。 如果你在博客之外阅读这篇文章,你可以在介绍文章的顶部找到这个系列中所有文章的链接。 也可订阅 RSS 频道。

练习1

实现 drop 非常简单。 我们需要一个函数,该函数允许将一个值移入其中,然后对该值不执行任何操作。

fn drop<T>(_: T) {
}

令人惊讶的是:查看实际的 drop 功能

练习2

实现此方法语法的重要技巧是,第一个参数必须是 self 的某种形式。 我们希望保持它作为一个不可变的引用,所以我们使用 &self。

#[derive(Debug)]
struct Foobar(i32);

impl Drop for Foobar {
    fn drop(&mut self) {
        println!("Dropping a Foobar: {:?}", self);
    }
}

impl Foobar {
    fn use_it(&self) {
        println!("I consumed a Foobar: {:?}", self);
    }
}

fn main() {
    let x = Foobar(1);
    println!("Before uses_foobar");
    x.use_it();
    x.use_it();
    println!("After uses_foobar");
}

您可能想知道:为什么代码使用 x.use_it () ? 它需要一个 Foobar 的引用,但是 x 是 Foobar! 事实上,你可能会写下这样的东西:

fn main() {
    let x = Foobar(1);
    println!("Before uses_foobar");
    (&x).use_it();
    (&x).use_it();
    println!("After uses_foobar");
}

虽然这是完全有效的代码,但也是不必要的: Rust 在方法调用的情况下会自动获取对某个值的引用。

练习3

原始代码不能编译,因为我们的 x 值被移动了。 但是,如果我们有一个 Copy 实现,Rust 会自动为我们创建一个值的副本。 正如我刚才提到的,你可以通过使用自动派生很容易做到这一点:

#[derive(Debug, Clone, Copy)]
struct Foobar(i32);

fn uses_foobar(foobar: Foobar) {
    println!("I consumed a Foobar: {:?}", foobar);
}

fn main() {
    let x = Foobar(1);
    uses_foobar(x);
    uses_foobar(x);
}

如果您想要更加明确,您可以直接编写实现。 请注意,据我所知,这样做没有任何好处(至少在这种情况下):

impl Clone for Foobar {
    fn clone(&self) -> Self {
        Foobar(self.0)
    }
}
impl Copy for Foobar {
}

Copy trait 不需要内容。 它唯一的作用是向编译器发出信号,告诉它可以在需要时进行复制。

练习4

在调用 double 时,我们没有引用 x,所以 double 函数需要一个实际的 Foobar,而不是一个引用。 它还必须返回一个 Foobar。 我们可以想出的一个实现是:

fn double(foobar: Foobar) -> Foobar {
    Foobar(foobar.0 * 2)
}

这会引入一个不可变的 Foobar,然后从其中的值构造一个新的 Foobar。 然而,另一种选择是改变最初的 Foobar,然后将其归还:

fn double(mut foobar: Foobar) -> Foobar {
    foobar.0 *= 2;
    foobar
}

我内心的 Haskeller 更喜欢第一个实现,因为它的变异较少。 我体内的巨魔喜欢第二个,因为它让我嘲笑了Haskeller。

最后更新于