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

Mutable reference to a tuple as input parameter

我好像掉进了这个叫做"fighting the borrow checker"的坑里。我有以下功能:

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).

这是第一个错误:

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{}));
}

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

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)
}

这是一个 Playpen and here's a version,它保留了 play 签名。

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

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

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 {})
}

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

您必须注意,您不能改变不可变数据。您可以使用自己的可变克隆,也可以在不进行突变的情况下工作。使 play 中的 decks 可变是获得对 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
    }
}

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