[][src]Crate multiref

Multireferences (aka the inverse distributive law)

Ever wanted to get &[T] from [&T] without cloning anything?

Semantically, it doesn't make sense (because a slice wraps a block of contiguous elements). But sometimes it's very convenient to be able to “algebraically” extract a common lifetime from a bunch of references.

This crate provides two helper types Slice and Pair that allow the following conversions:

Moreover, each of these types provides .as_ref() and .as_mut() methods (with signatures different from the ones used by the AsRef and AsMut traits) implementing the forward distributive law:

Motivation

The following text is somewhat long. Unfortunately, I do not know any realistic uses of the inverse distributive law in situations not involving a formal argument in a contra-contravariant position.

Preliminaries

Suppose you have the following trait:

trait Info {
    type RelevantPart: ?Sized;

    fn info<E, Info>(&self, extractor: E) -> Info where
        E: FnOnce(&Self::RelevantPart) -> Info;
}

I.e. a type implementing Info can temporarily give access to some its part. For example:

struct Configuration {
    fields: HashMap<String, String>,
}

impl Info for Configuration {
    type RelevantPart = str;
     
    fn info<E, Info>(&self, extractor: E) -> Info where
        E: FnOnce(&str) -> Info 
    {
        match self.fields.get("name") {
            Some(name) => extractor(name),
            None       => extractor("UNKNOWN"),
        }
    }
}

If you are interested whether the continuation-passing style is necessary, try to write a non-cps equivalent

fn info<'a>(&'a self) -> &'a str 

for some dynamically generated string (e.g. the current timestamp) instead of static "UNKNOWN".

The only safe way to get the &'a str from such a string seems to be to embed this string directly in the Configuration. But it can't be done through a shared reference (and if it could, it would be a rather strange-looking solution, because this string has nothing to do with the configuration).

The problem

Now suppose that you want to give two fields to the extractor. What the RelevantPart would be?

The laziest solution is to define

type RelevantPart = (String, String);

But such a type requires cloning the strings. It would be better to have

type RelevantPart = (&'a str, &'a str);

but our trait doesn't have the 'a parameter. And if it had it would not work either. E.g. a &str borrowed from a dynamically generated analogue of "UNKNOWN" must have its lifetime fully contained in the info method. But the 'a lifetime is external to this method.

A solution

fn make_error_string() -> String { unimplemented!() }

use multiref::Pair; // a wrapper around (&'a A, &'a B)

impl Info for Configuration {
    type RelevantPart = Pair<str, str>; // now this type supports any lifetime
     
    fn info<E, Info>(&self, extractor: E) -> Info where
        E: FnOnce(&Pair<str,str>) -> Info 
    {
        let error_string = make_error_string();
        // for simplicity we generate an error string unconditionally

        let foo: &str = match self.fields.get("foo") {
            Some(foo) => &foo,
            None      => &error_string,
        };

        let bar: &str = match self.fields.get("bar") {
            Some(bar) => &bar,
            None      => &error_string,
        };

        extractor( (&(foo, bar)).into() )
        // Pair::new(&(foo, bar)) also can be used
    }
}

Warning

This crate uses some unsafe code with questionable soundness. It seems that this code is sound if DST are sound, but it may not be the case.

Structs

Pair

A wrapper for a pair of references.

Slice

A wrapper for a slice of references.