Hello everyone,

I'm playing around with a Rust implementation for Cap'n Proto. Check
it out: http://github.com/dwrensha/capnproto-rust . I welcome any
comments or contributions.

The reason I'm sharing this project with you now is I'd like to ask
about a language feature missing from Rust that I feel may be
hindering my progress.

To the point, I want to be able define a trait like this:

trait Constructable<'self> {
   fn construct<'a>(StructReader<'a>) -> Self<'a>;
}

The important detail here is that the lifetime 'a of the constructed
object is the same as the lifetime of the constructor's input.

Why do I want this?

When we are reading a Cap'n Proto message, the message's data is just
an immutable byte vector owned by some enclosing scope. To traverse
the message, we use auxiliary data structures such as
`StructReader<'a>` and `ListReader<'a>` that hold slices of the
message and various indices into them. These auxiliary structures are
internal to the library and should not be exposed to the user. They
are in turn wrapped by specialized user-facing structs. For example,
if AddressBook is a user-defined message, then capnproto-rust
generates an `AddressBook::Reader<'a>` struct, with message-specific
accessor methods, which can be constructed from a generic
`StructReader<'a>`.

Here we hit a problem. We want the user to be able to do something
like this:

let addressBookReader = message.readRoot::<AddressBook::Reader>();

If we had the `Constructable` trait above, we could achieve that like
this:

impl <'self> MessageReader <'self> {
   // ...
  fn readRoot<T : Constructable> (&self) -> T<'a> {
     Constructable::construct(self.readRootStructReader())
  }
}

but otherwise the user is forced to do something like:

let addressBookReader =
AddressBook::Reader::new(message.readRootStructReader());

or maybe, if we want to hide the `StructReader` as much a possible:

let addressBookReader = AddressBook::Reader::constructFromMessageRoot(message);

Note that the latter workaround requires that capnproto-rust generate a
`constructFromMessageRoot` method for each type of message.

We run into more problems when we start considering lists. Cap'n Proto
messages may contain arbitrarily nested lists. So, for example, we may
encounter a list of lists of AddressBooks. Under the hood, these will
be generic ListReaders, but we would like to provide users an appropriate
specialized interface to them. For instance, if a user gets an element
from a list of structs, it should not be a generic StructReader---it
should be a reader of the appropriate specialized type.

As a simple example, we want allow uses like this:

let people = addressBookReader.getPeople();
for std::uint::range(0, people.size()) |i| {
    let person = people.get(i);
    printfln!("person: %s, email: %s",
               person.getName(), person.getEmail());
}


Generalizing such usage to the general case of arbitrarily nested
lists seems to require a trait like `Constructable`. Otherwise I can't
see a way around writing a tangled mess of `constructFrom...` methods.
Just ask yourself: what type does `people` need to be so that it can
implement the `get` method? It seems to me that the natural answer is
something like "the instantiation of some struct with a type parameter
<T : Constructable>".

I'll note that my implementation strategy has been to follow the C++
Cap'n Proto implementation, which uses templates to solve all of these
problems.

My big questions are: Is there a way to do what I want in Rust? If
not, why not?---and will there be in the future?

Thanks,
David
_______________________________________________
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to