Info
This post is auto-generated from RSS feed The Rust Programming Language Forum - Latest topics. Source: Declaring an associated type that holds references to self's fields
I initially had the following code for an Interface
trait for use in displaying text to a user and getting input back. This includes a separate InterfaceProvider
trait that passes a &mut Interface
to a supplied function, making it possible to ensure that any necessary startup & cleanup code ran before & after using the interface. (I haven't yet implemented any interfaces that need startup or cleanup, but I'm thinking ahead.)
use std::io::{self, BufRead, Write};
pub trait InterfaceProvider {
type Interface: Interface;
/// Run `func` with the associated `Interface`.
fn with_interface<F>(self, func: F) -> io::Result<()>
where
F: FnOnce(&mut Self::Interface) -> io::Result<()>;
}
pub trait Interface {
/// Display the given text in the interface.
fn show_output(&mut self, text: &str) -> io::Result<()>;
/// Read a line of input from the interface.
///
/// Returns `None` on end of input.
fn get_input(&mut self) -> io::Result<Option<String>>;
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BasicInterface<R, W> {
reader: R,
writer: W,
wrote_prompt: bool,
wrote_last_output: bool,
}
impl<R, W> BasicInterface<R, W> {
pub fn new(reader: R, writer: W) -> Self {
BasicInterface {
reader,
writer,
wrote_prompt: false,
wrote_last_output: false,
}
}
}
impl<R: BufRead, W: Write> InterfaceProvider for BasicInterface<R, W> {
type Interface = Self;
fn with_interface<F>(mut self, func: F) -> io::Result<()>
where
F: FnOnce(&mut Self::Interface) -> io::Result<()>,
{
func(&mut self)
}
}
impl<R: BufRead, W: Write> Interface for BasicInterface<R, W> {
fn show_output(&mut self, text: &str) -> io::Result<()> {
if self.wrote_prompt {
writeln!(&mut self.writer)?;
}
if !text.is_empty() {
writeln!(&mut self.writer, "{text}")?;
self.wrote_last_output = true;
} else {
self.wrote_last_output = false;
}
Ok(())
}
fn get_input(&mut self) -> io::Result<Option<String>> {
if self.wrote_last_output {
writeln!(&mut self.writer)?;
}
write!(&mut self.writer, "> ")?;
self.writer.flush()?;
self.wrote_prompt = true;
let mut input = String::new();
if self.reader.read_line(&mut input)? != 0 {
Ok(Some(input))
} else {
// Force the start of a new line:
writeln!(&mut self.writer)?;
Ok(None)
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StandardInterface;
impl InterfaceProvider for StandardInterface {
type Interface = BasicInterface<io::StdinLock<'static>, io::StdoutLock<'static>>;
fn with_interface<F>(self, func: F) -> io::Result<()>
where
F: FnOnce(&mut Self::Interface) -> io::Result<()>,
{
let mut iface = BasicInterface::new(io::stdin().lock(), io::stdout().lock());
func(&mut iface)
}
}
As you can see, InterfaceProvider::with_interface()
consumes the provider, as I wanted to keep things simple and lifetime-free at first. Now, however, I'm ready to make things complicated and lifetime-full, but I've gotten a bit over my head.
Specifically, I want to make the following changes:
Change the receiver of with_interface()
to &mut self
(not &self
, as the interface may borrow some data from the provider, and the interface has to be mutable, so the provider should be as well)
Split BasicInterface
into:
BasicInterfaceProvider
that only implements InterfaceProvider
and just stores the reader & writerBasicInterface
that only implements Interface
and stores mutable references to the provider's reader & writer plus the state fieldsChange the Interface
for StandardInterface
to the new BasicInterface
, still parametrized by stdin & stdout
My best (as in, producing the fewest errors) attempt so far has been as follows, where I changed the with_interface()
receiver, split BasicInterface
apart, and tried (and failed) to define BasicInterfaceProvider::Interface
as a BasicInterface
parameterized by the lifetime of self
:
I also tried the following, inspired by the unstable Pattern
trait and its Searcher
associated type. I changed the definition of InterfaceProvider::Interface
to type Interface<'a>: Interface<'a>
, changed the receiver for with_interface()
to &mut self
, added <'_>
to the &mut Self::Interface
argument to the FnOnce
passed to with_interface()
, split BasicInterface
apart, defined BasicInterfaceProvider::Interface<'a>
as a BasicInterface<'a, R, W>
, and added some lifetimes to the bounds of impl InterfaceProvider for BasicInterfaceProvider
based on the compiler's suggestions, but I still can't get it to compile.
I've tried minor variations on both of the above attempts, but I can't get anything that compiles. Help?
1 post - 1 participant
🏷️ rust_feed