Generic function that requires one of two traits to be implemented

I'm writing to a generic "file", but in case of an error, i want to rewind the file to the start in the case of an error (in order to retry), but if it doesn't support rewinding, i just want to return the error without retrying.

to make this more difficult, ErrorKind::NotSeekable is unstable.

In Rust, changing behavior depending on if a trait is implemented is called specialization, and isn't currently possible.

A likely solution is to make two functions, one that requires Seek and one that does not.

1 Like

i know about specialization. it is possible on nightly.

i'm looking for an alternative, perhaps something like the typestate pattern.

that's not a viable option, this function is the main entrypoint to my codebase. i would have to duplicate the entire codebase, or i would have to make the two functions wrappers around the actual solution.

You could make a type that always errors.

use std::io::{self, Result, Seek, SeekFrom};

struct NoSeek<S>(S);

impl<S> Seek for NoSeek<S> {
    fn seek(&mut self, _pos: SeekFrom) -> Result<u64> {
        Err(io::Error::new(io::ErrorKind::Unsupported, "cannot seek"))
    }
}

pub fn entrypoint_no_seek<S>(s: S) {
    entrypoint(NoSeek(s));
}

pub fn entrypoint<S: Seek>(s: S) {
    todo!()
}
2 Likes

What if you split it into two traits: one for generic file ops, and one for error handling?

trait File {
    fn write(&mut self) -> Result<(), Error>;
}

trait FileWithRewind: File {
    fn rewind();
}

trait HandleError {
    fn on_error(&mut self) -> Result<(), Error>;
}

fn mega_function<F: File + HandleError>(file: &mut F) -> Result<(), Error> {
    if let Err(e) = file.write() {
        file.on_error()?;
    }
    Ok(())
}

struct SimpleFile;

impl File for SimpleFile {
    fn write(&mut self) -> Result<(), Error> {
        Err(Error::from(ErrorKind::Other))
    }
}

struct RewindableFile;

impl File for RewindableFile {
    fn write(&mut self) -> Result<(), Error> {
        Err(Error::from(ErrorKind::Other))
    }
}

impl FileWithRewind for RewindableFile {
    fn rewind() {}
}

impl HandleError for RewindableFile {
    fn on_error(&mut self) -> Result<(), Error> {
        RewindableFile::rewind();
        Ok(())
    }
}

impl HandleError for SimpleFile {
    fn on_error(&mut self) -> Result<(), Error> {
        Err(Error::from(ErrorKind::Unsupported))
    }
}
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.