3

I am playing around with Rust's capnproto library. Because Rust can infer types in some situations, I can do things like this:

let mut message = ::capnp::message::Builder::new_default();

Without having to know the type of message. If I want to pass a reference to message into a function, I now need to know what message is to let the function know what to expect.

Is there a convenient way to do this in general?

So far I have done the following:

let testing: () = message;

which fails with the compiler error:

error[E0308]: mismatched types
   --> src/main.rs:197:18
    |
197 |                 let temp: () = message;
    |                           ^^^^^^^ expected (), found struct `capnp::message::Builder`

But when I type annotate my function as follows:

fn example_fn(message: capnp::message::Builder) {...}

I get an error like:

error[E0243]: wrong number of type arguments: expected 1, found 0
  --> src/main.rs:72:32
   |
72 | fn dump_capnp_to_file(message: capnp::message::Builder, filename: &str) {
   |                                ^^^^^^^^^^^^^^^^^^^^^^^ expected 1 type argument

error: aborting due to previous error

I'm very new to Rust coming from a C++ background; sorry if this a rookie question!

JMzance
  • 1,704
  • 4
  • 30
  • 49
  • Do you care what the type argument is? https://docs.capnproto-rust.org/capnp/message/struct.Builder.html#method.new_default – Josh Lee May 01 '17 at 17:42
  • 5
    Please review how to create a [MCVE]. Also include the *complete* error message. For example, those `^^^` *point at something important* but we can't tell what. – Shepmaster May 01 '17 at 17:49
  • 2
    *coming from a C++ background* — you're trying to pass a `vector` instead of a `vector` — does that help? – Shepmaster May 01 '17 at 17:57
  • @Shepmaster the ^^^s point at capnp::message::Builder. Should I assume that by `vector` you mean that 'Builder' is a template and needs further typing information? – JMzance May 01 '17 at 18:43
  • Please [edit] your question to have the *complete error message*; comments are not for additional information. – Shepmaster May 01 '17 at 18:44
  • See updated question – JMzance May 01 '17 at 18:48
  • I think I figured out your problem (see my answer), but I also think you replaced the wrong error with your edit. Please reread it and make sure you're posting both error messages in their entirety – trent May 01 '17 at 19:48

1 Answers1

2

Rust will not infer types in function parameter position. This is by design, as the Rust language FAQ states:

Why aren't function signatures inferred?

In Rust, declarations tend to come with explicit types, while actual code has its types inferred. There are several reasons for this design:

  • Mandatory declaration signatures help enforce interface stability at both the module and crate level.

  • Signatures improve code comprehension for the programmer, eliminating the need for an IDE running an inference algorithm across an entire crate to be able to guess at a function’s argument types; it’s always explicit and nearby.

  • Mechanically, it simplifies the inference algorithm, as inference only requires looking at one function at a time.

Since capnp::message::Builder<A> takes a type parameter A, you need to qualify the type of the parameter by giving A a value:

fn dump_capnp_to_file(message: capnp::message::Builder<SomeType>, filename: String) {
//                                                    ^^^^^^^^^^

or else make your function also generic so it can accept any type A:

fn dump_capnp_to_file<A>(message: capnp::message::Builder<A>, filename: String) {
//                   ^^^                                 ^^^

Putting bounds on A

If you take the last option, you might want additional trait bounds to allow you to do different things with message inside the function. For example, you might want to send message to another thread, which requires that Builder<A> implement Send. Builder has the following impl (reference):

impl <A> Send for Builder<A> where A: Send + Allocator

which means that Builder<A> can implement Send, but only when A implements Send and Allocator. You can make that your own bound (requirement) on A:

fn dump_capnp_to_file<A>(message: capnp::message::Builder<A>, filename: String)
    where A: Send + Allocator
{
    // multi-threaded code...
}

Alternatively (and maybe slightly better), bound Builder<A> with Send directly:

fn dump_capnp_to_file<A>(message: capnp::message::Builder<A>, filename: String)
    where capnp::message::Builder<A>: Send

Then you will only be able to call dump_capnp_to_file on a Builder that implements Send.

Community
  • 1
  • 1
trent
  • 25,033
  • 7
  • 51
  • 90
  • Could you clarify what you mean by bounds on A please? – JMzance May 02 '17 at 19:54
  • 1
    @JMzance I'll do that if you fix the error messages in your question. Deal? ;-) – trent May 02 '17 at 20:29
  • 1
    @JMzance Done. This actually isn't a great example of when to use trait bounds, but when I originally answered I couldn't access the capnproto documentation to know that so I guessed it might be desirable – trent May 05 '17 at 01:13