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

jz8*_*z87 2 rust

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

obj.do_x()
   .do_y()
Run Code Online (Sandbox Code Playgroud)

在Rust中我遇到了一个问题,if do_x,do_ytake &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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是,new()返回a Counter,但是流畅的API需求mut Counter.

Mat*_* M. 9

你为什么要使用参考?

#[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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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


Sim*_*ead 6

你是对的,类型不同.你可能会遇到终身问题.我假设你试过这个:

let mut counter = Counter::new().incr().incr();
Run Code Online (Sandbox Code Playgroud)

那失败了.然而,拆分出来:

let mut counter = Counter::new();
counter.incr().incr();
Run Code Online (Sandbox Code Playgroud)

..工作良好.编译器实际上会给你一个体面的提示:

help: consider using a `let` binding to increase its lifetime
Run Code Online (Sandbox Code Playgroud)

正如我在评论中所说的那样,在对象实例化期间"解决"这种流畅的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);
}
Run Code Online (Sandbox Code Playgroud)

从本质上讲,这可以通过在build调用之后放弃借用来解决生命周期问题(它注意到你已经完成了a的实例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
Run Code Online (Sandbox Code Playgroud)