无法创建流畅的API,因为类型“self”与“&mut self”不匹配。

5

在许多其他语言中,通常会有类似以下的API:

obj.do_x()
   .do_y()

在Rust中,我遇到了一个问题,如果do_xdo_y使用&mut self,则类型不匹配。有没有一种优雅的方法来解决这个问题?
例如:
#[derive(Debug)]
struct Counter { count: u32 }
impl Counter {
    fn incr(&mut self) -> &mut Counter { 
        self.count = self.count + 1; 
        self 
    }

    fn new() -> Counter {
        Counter {
            count: 0
        }
    }
}

问题在于,new()返回一个Counter,但是流畅的API需要一个mut Counter

你需要展示一个实际的例子,让我们能够向你展示为什么它不起作用。这在 Rust 中很常见...人们称之为“构建器模式”。举个随机的例子,这是我刚刚在谷歌上找到的一个:https://gist.github.com/Nogbit/aa0ac67e7208850b94e2 - Simon Whitehead
2个回答

11

你为什么要使用引用?

#[derive(Debug)]
struct Counter { count: u32 }

impl Counter {
    fn incr(mut self) -> Counter { 
        self.count = self.count + 1; 
        self 
    }

    fn new() -> Counter {
        Counter {
            count: 0
        }
    }
}

如果您使用原始的版本,则不再存在借用问题:)

注意:虽然适用于构建者模式,但对于常规类型可能会很烦人。


我考虑过这个问题,但不确定原帖作者想要什么。无论如何,+1,这是解决即时问题的实际方法。 - Simon Whitehead
1
@SimonWhitehead:我也不太确定,但由于它与当前主流语言相比有些奇特,所以我认为值得一提 :) - Matthieu M.
1
嗯,这会导致很多副本吗?还是Rust比那更聪明? - jz87
1
@jz87:从语言角度来看,不是。但目前它被降级为memcpy,优化器可能无法智能地删除它们(逐个案例)。pcwalton正在使用LLVM更积极地删除无用的memcpy调用,并且有一个第一补丁,可以删除Servo中约25%的memcpy调用(这很令人印象深刻,因为其中可能有一些是有用的)...但是在审查中建议他将此分析移至其他地方,虽然它会减慢集成速度,但实际上可能会提高省略调用站点的数量。 - Matthieu M.

10

你是正确的,这两种类型确实不同。你可能会遇到生命周期问题。我猜你已经尝试过这个了:

let mut counter = Counter::new().incr().incr();

那样是失败的。然而,将它们分开:

let mut counter = Counter::new();
counter.incr().incr();

这很好用。编译器实际上会给你一个不错的提示:

help: consider using a `let` binding to increase its lifetime

就像我在评论中说的那样,为了“解决”在对象实例化过程中的流畅API设计问题,人们通常会创建一个Builder类型(即“建造者模式”)。例如,您的建造者可能看起来像这样:

#[derive(Debug)]
struct Counter { count: u32 }

struct CounterBuilder { count: u32 }
impl CounterBuilder {
    fn new() -> CounterBuilder {
        CounterBuilder {
            count: 0
        }
    }

    fn incr(&mut self) -> &mut CounterBuilder { 
        self.count = self.count + 1; 
        self 
    }

    fn build(&self) -> Counter {
        Counter {
            count: self.count
        }
    }
}

fn main() {
    let counter = CounterBuilder::new()
        .incr()
        .incr()
        .build();

    println!("Counter value is: {}", counter.count); // Should print 2
    assert_eq!(2, counter.count);
}

本质上,这个方法通过在build调用后丢弃借用来解决生命周期问题(这表明您已经完成了CounterBuilder实例)...此时,您将获得一个新的具体Counter对象并准备就绪。

一旦您实例化了Counter...您可以通过其自己的流畅API进行修改,例如:

let mut counter = CounterBuilder::new()
        .incr()
        .incr()
        .build();

counter.local_increment().local_increment(); // For example, if you were to add a "local_increment" fluent method to Counter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接