Оборачивание RefCell и Rc в тип структуры - PullRequest
0 голосов
/ 07 сентября 2018

Я хотел бы иметь структуру, которая имеет поле для записи, но явно заимствовано:

struct App<W: Clone<BorrowMut<Write>>> {
    stdout: W,
}

... так что он может использовать его для внутреннего использования:

impl<W: Clone<BorrowMut<Write>>> App<W> {
    fn hello(&mut self) -> Result<()> {
        Rc::clone(&self.stdout).borrow_mut().write(b"world\n")?;
        Ok(())
    }
}

Я пытался передать ему курсор и затем использовать его:

let mut cursor = Rc::new(RefCell::new(Cursor::new(vec![0])));
let mut app = App { stdout: cursor };
app.hello().expect("failed to write");

let mut line = String::new();
Rc::clone(&cursor).borrow_mut().read_line(&mut line).unwrap();

Ржавчина лает:

error[E0107]: wrong number of type arguments: expected 0, found 1
 --> src/bin/play.rs:6:21
  |
6 | struct App<W: Clone<BorrowMut<Write>>> {
  |                     ^^^^^^^^^^^^^^^^ unexpected type argument

Моя конечная цель: передать stdin, stdout и stderr в App структуру. В fn main это будет настоящий stdin / stdout / stderr. В тестах это могут быть курсоры. Поскольку мне нужно получить доступ к ним за пределами App (например, в тестах), мне нужно несколько владельцев (таким образом, Rc) и изменяемые заимствования во время выполнения (таким образом, RefCount).

Как я могу это реализовать?

1 Ответ

0 голосов
/ 07 сентября 2018

Это не то, как вы применяете несколько ограничений к параметру типа. Вместо этого вы используете оператор +, например: <W: Clone + Write + BorrowMut>

Но если вы хотите, чтобы BorrowMut была абстракцией для RefCell, это не сработает. borrow_mut метод RefCell не является частью какой-либо черты, поэтому вам нужно будет зависеть от RefCell непосредственно в вашей структуре данных:

struct App<W: Clone + Write> {
    stdout: Rc<RefCell<W>>,
}

Сказав это, считается наилучшей практикой не устанавливать ненужные ограничения на структуру. Вы можете оставить их здесь, и просто упомянуть их на impl позже.

struct App<W> {
    stdout: Rc<RefCell<W>>,
}

Чтобы получить доступ к содержимому Rc, необходимо разыменовать с помощью *. Это может быть немного сложно в вашем случае, потому что есть одеяло impl из BorrowMut, что означает, что Rc имеет другой borrow_mut, который вам определенно не нужен.

impl<W: Clone + Write> App<W> {
    fn hello(&mut self) -> Result<()> {
        (*self.stdout).borrow_mut().write(b"world\n")?;
        Ok(())
    }
}

Опять же, когда вы используете это, вам нужно разыменовать Rc:

let cursor = Rc::new(RefCell::new(Cursor::new(vec![0])));
let mut app = App { stdout: cursor.clone() };
app.hello().expect("failed to write");

let mut line = String::new();

let mut cursor = (&*cursor).borrow_mut();
// move to the beginning or else there's nothing to read
cursor.set_position(0);
cursor.read_line(&mut line).unwrap();

println!("result = {:?}", line);

Также обратите внимание, что Rc был клонирован в курсор. В противном случае он будет перемещен, и вы не сможете использовать его позже.

...