[−][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:
&'a [&'x T] -> &'a Slice<T>
(and a mutable equivalent)&'a (&'x A, &'x B) -> &'a Pair<A, B>
(and a mutable equivalent)
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:
&'a Slice<T> -> &'a [&'a T]
(and a mutable equivalent)&'a Pair<A, B> -> &'a (&'a A, &'a B)
(and a mutable equivalent)
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. |