rust-ipfs-api icon indicating copy to clipboard operation
rust-ipfs-api copied to clipboard

remove 'static lifetime for add() argument

Open raffomania opened this issue 5 years ago • 6 comments

When using add, AFAIK the data I pass in has to be moved into the method because the argument requires a 'static lifetime. This makes the following impossible:

    let mut child = Command::new("cat")
        .arg("test.txt")
        .stdout(Stdio::piped())
        .spawn()?;

    let hash = ipfs.add(child.stdout?);
    child.wait()?; // error: child was moved in the previous line
    hash

What I want to do:

  1. directly pipe stdout into ipfs, without waiting
  2. then wait for the first command to finish and check if it returned status 0
  3. then return the ipfs hash

please correct me if I'm wrong, but with a 'static lifetime I think there's no way to do this.

raffomania avatar Mar 23 '20 12:03 raffomania

Sorry for the late response. I think this might be a requirement for hyper. See this related issue: https://github.com/ferristseng/rust-multipart-rfc7578/issues/4

ferristseng avatar May 20 '20 19:05 ferristseng

The 'static requirement on data is extremely limited and makes the function useless because the usecase of adding compile time data to IPFS only exists in example code. I'm reverting to using the ipfs cli in my project because of this issue.

rand0m-cloud avatar Dec 12 '21 18:12 rand0m-cloud

I believe this lifetime is a restriction of Actix/Hyper (example: https://docs.rs/hyper/0.14.16/hyper/struct.Body.html#method.wrap_stream).

What exactly are you trying to do?

ferristseng avatar Dec 18 '21 17:12 ferristseng

please correct me if I'm wrong, but with a 'static lifetime I think there's no way to do this.

Added an example of how to do this: https://github.com/ferristseng/rust-ipfs-api/blob/35d282f4cfb2cf083abff5efa60c3c916f1d13fa/ipfs-api-examples/examples/add_file_piped_from_command.rs

ferristseng avatar Dec 18 '21 18:12 ferristseng

Thanks for following up! I'm not sure how you're able to call take without any arguments in that example, as the docs say it should take an u64 as a limit. Also, I don't understand how that example solves the lifetime problems I encountered, and at this point I don't remember the exact errors I found, so maybe I made an error unrelated to the lifetime constraints of add. I'll try to incorporate the example into my project and report back, however I'm not sure when I'll get around to that.

raffomania avatar Dec 19 '21 15:12 raffomania

Reviving this: I'm trying to cat data from IPFS and re-feed it to add with a different hash function, both via the HTTP API.

In the optimal case I don't want to read all of this into memory and then re-add it. Best case scenario would be to plumb the output of cat directly into add_async_with_options.

The slightly-worse-but-probably-works-for-now solution is to read all of it into memory and then add it, something like this:

let cat_resp = client
                    .cat(cid)
                    .map_ok(|chunk| chunk.to_vec())
                    .try_concat()
                    .await
                    .context("unable to cat file")?;

let sha1_resp = client.add_async_with_options(cat_resp.as_slice(),opts).await?;

But that does not work:

error[E0597]: `cat_resp` does not live long enough
   --> src/bin/test-api.rs:198:63
    |
198 |                 let sha1_resp = client.add_async_with_options(cat_resp.as_slice(),opts).await?;
    |                                 ------------------------------^^^^^^^^^^^^^^^^^^^------
    |                                 |                             |
    |                                 |                             borrowed value does not live long enough
    |                                 argument requires that `cat_resp` is borrowed for `'static`
199 |             }
    |             - `cat_resp` dropped here while still borrowed

For more information about this error, try `rustc --explain E0597`.

Edit: Looking a bit, I don't think the multipart::Form stuff needs a static lifetime:

impl<'a> Form<'a> {
    pub fn add_async_reader<F, R>(&mut self, name: F, read: R)
    where
        F: Display,
        R: 'a + AsyncRead + Send + Sync + Unpin,
    {
// ...
    }

// ...
}

Edit2: It does work with add_with_options (without AsyncRead), wrapping the Vec<u8> in a Cursor... hmm.

mrd0ll4r avatar Sep 22 '22 11:09 mrd0ll4r