-1

I am attempting to work with futures to look up asynchronously look up a value. If that value exists I want to return it and if it does not I want to create it.

// A network will be created unless one already exists with this name
pub fn build_network(name: &'static str) {
    let docker = Docker::new();

    // Get a list of networks
    let fut = docker
        .networks()
        .list(&Default::default())
        .and_then(move |networks| {
            // Check if a network with the given name exists  
            let network = networks.iter().find(|n| n.name == name);

            match network {
                // Pass on network
                Some(net) => future::ok(net.id),
                // Create a new network
                None => {
                    let docker = Docker::new();
                    docker
                        .networks()
                        .create(&NetworkCreateOptions::builder(name).driver("bridge").build())
                        .map(|new_net| new_net.id)

                }
            }
        })
        .map(move |net| {
            println!("{:#?}", net);
        })
        .map_err(|e| eprintln!("Error: {}", e));

    tokio::run(fut);
}

It looks like there is a type mismatch in my match expression. I am trying to make sure each arm contains a future for a shiplift network struct, but it looks like I'm not quite getting it.

error[E0308]: match arms have incompatible types
   --> src/up/mod.rs:168:13
    |
168 | /             match network {
169 | |                 // Pass on network
170 | |                 Some(net) => future::ok(net.id),
171 | |                 // Create a new network
...   |
178 | |                 }
179 | |             }
    | |_____________^ expected struct `futures::FutureResult`, found struct `futures::Map`
    |
    = note: expected type `futures::FutureResult<std::string::String, _>`
               found type `futures::Map<impl futures::Future, [closure@src/up/mod.rs:177:30: 177:50]>`
note: match arm with an incompatible type
   --> src/up/mod.rs:172:25
    |
172 |                   None => {
    |  _________________________^
173 | |                     let docker = Docker::new();
174 | |                     docker
175 | |                         .networks()
176 | |                         .create(&NetworkCreateOptions::builder(name).driver("bridge").build())
177 | |                         .map(|new_net| new_net.id)
178 | |                 }
    | |_________________^

error: aborting due to previous error
BBS
  • 1,351
  • 2
  • 12
  • 27

1 Answers1

1

Your clearest mistake is the ; at the end of the None branch. A semicolon always discards the value of the previous expression, and a block ending with a semicolon has type () (assuming the end is reachable).

After removing the ;, you’ll see that the types still don’t match. The Some branch has type FutureResult<&NetworkDetails>, while the None branch now has type impl Future<Item = NetworkCreateInfo>. I’m not sure what you’re trying to do here, since even the base NetworkDetails and NetworkCreateInfo types are incompatible. You’ll need to figure out what type you want and how to get the same type in both branches.

Edit for updated question: Okay, you want to get a String out of both branches. You have two different types that both implement the Future<Item = String> trait, and you need both branches to be of the same type. This is exactly the purpose of future::Either. Simply wrap one branch in Either::A and the other branch in Either::B.

After that, you’ll also find a trivial borrowing problem in the first branch: you’ll need to copy the string with net.id.clone().

Anders Kaseorg
  • 3,657
  • 22
  • 35
  • I have edited the question based on this. Considering `NetworkDetails` and `NetworkCreateInfo` are not types I think the best option is to return the network id as a `&str`. – BBS Jan 08 '19 at 06:44