可变引用元组作为输入参数

时间:2016-12-18 10:06:57

标签: rust borrow-checker

我似乎陷入了被称为"与借阅检查者打架的洞#34;在这一个。我有以下功能:

fn draw_pair(decks: &(&mut Deck, &mut Deck)) -> (Card, Card) {
    let (&mut p1, &mut p2) = decks;

    (p1.draw_card(), p2.draw_card())
}

我收到以下错误:

expected type: &(&mut Deck, &mut Deck)
found type: (_, _)

这个想法是将可变引用作为元组的内容。我认为没有理由改变元组本身。该函数将循环运行。

我尝试过编写let &(&mut p1, &mut p2) = decks;,但它告诉我它无法摆脱借来的内容。

这是调用draw_pair的函数:

fn play(decks: (Deck, Deck)) {
    loop {
        let cards = draw_pair(&decks);
        // actual game not yet implemented
    }
}

这也给了我一个错误,说它预计会&(&mut Deck, &mut Deck),但会得到&(Deck, Deck)

3 个答案:

答案 0 :(得分:1)

由于您无法摆脱借来的可变引用,因此您必须克隆这些字段。

fn main() {
    play((&mut Deck(0), &mut Deck(0)));
}

#[derive(Clone)]
struct Deck(i32);

fn play(decks: (&mut Deck, &mut Deck)) {
    let cards = draw_pair(&decks);
}

fn draw_pair(decks: &(&mut Deck, &mut Deck)) -> (i32, i32) {
    let mut p1 = decks.0.clone();
    let mut p2 = decks.1.clone();
    (0, 0)
}

此处有一个保留play签名的Playpenhere's a version

老实说,我认为在这种情况下,添加一个新类型来表示元组将是一个更清晰和可读的解决方案。

理想情况下,我会这样做:

struct Deck {}
struct Card {}

#[derive(Debug)]
struct Pair<T> {
    first: T,
    second: T,
}

impl<T> Pair<T> {
    pub fn new(first: T, second: T) -> Pair<T> {
        Pair { first: first, second: second }
    }
}

fn play(decks: Pair<Deck>) {
    let mut decks = decks;
    let cards = draw_pair(&mut decks);
}

fn draw_pair(decks: &mut Pair<Deck>) -> Pair<Card> {
    Pair::new(Card {}, Card {})
}

答案 1 :(得分:1)

每当您想使用模式匹配和解构获取引用时,请使用ref instead of &。请改用let (ref mut p1, ref mut p2),然后取消引用deck

您必须注意,您不能改变不可变数据。您既可以使用自己的可变克隆,也可以在没有突变的情况下工作。在decks中使play可变是获取draw_pair中内部数据的可变引用的唯一方法。以下代码解决了您的问题:

fn draw_pair(decks: &mut (Deck, Deck)) -> (Card, Card) {
    let (ref mut p1, ref mut p2) = *decks;

    (p1.draw_card(), p2.draw_card())
}

fn play(mut decks: (Deck, Deck)) {
    loop {
        let cards = draw_pair(&mut decks);
        // actual game not yet implemented
    }
}

如果您在decks中收到的play对是不可变的,那么除了维护您自己的克隆和可变Deck之外没有别的办法了解@wimh在他的回答中。如果您想更简洁地创建自己的可变克隆,以下一个衬垫将有所帮助:&(&mut decks.0.clone(), &mut decks.1.clone())

答案 2 :(得分:0)

这是第一个错误:

8 |     let (&mut p1, &mut p2) = decks;
  |         ^^^^^^^^^^^^^^^^^^ expected reference, found tuple
  |
  = note: expected type `&(&mut Deck, &mut Deck)`
  = note:    found type `(_, _)`

解决这个问题的最简单方法是取消引用正确的大小(*decks),但是你得到了另一个错误:

8 |     let (&mut p1, &mut p2) = *decks;
  |          ^^^^^--
  |          |    |
  |          |    hint: to prevent move, use `ref p1` or `ref mut p1`
  |          cannot move out of borrowed content

正如使用ref暗示的那样:

let (&mut ref p1, &mut ref p2) = *decks;

但是没有必要完全解构左侧,你也可以使用

let (ref p1, ref p2) = *decks;

这是一个minimum implementation来重现您的问题,并应用了修复程序:

struct Card{}
struct Deck{}
impl Deck {
    fn draw_card(&self) -> Card { Card {}}
}

fn draw_pair(decks: &(&mut Deck, &mut Deck)) -> (Card, Card) {
    let (ref p1, ref p2) = *decks;

    (p1.draw_card(), p2.draw_card())
}

fn main() {
    println!("Hello, world!");
}

编辑:这是play函数的解决方案,无需修改它的签名:

struct Card{}

#[derive(Clone)]
struct Deck{}
impl Deck {
    fn draw_card(&self) -> Card { Card {}}
}

fn play(decks: (Deck, Deck)) {
    loop {
        let (ref deck1, ref deck2) = decks;
        let mut deck1 = deck1.clone();
        let mut deck2 = deck2.clone();
        let decks = (&mut deck1, &mut deck2);
        let cards = draw_pair(&decks);
        // actual game not yet implemented
    }
}

fn draw_pair(decks: &(&mut Deck, &mut Deck)) -> (Card, Card) {
    let (ref p1, ref p2) = *decks;

    (p1.draw_card(), p2.draw_card())
}

fn main() {
    play((Deck{}, Deck{}));
}