0

I have an external type (protobuf:::CodedInputStream) which expects a reference to a referenced trait-object (TcpStream as &mut Read).

I would like to make a type or function Connection wrap the TcpStream (taking ownership), together with a CodedInputStream, in order to use it's capabilities, encapsulating the use and ownership of the TcpStream as an internal detail.

However, I can't figure out how. My naive attempt (in pseudo-rust)

struct Connection {
    stream: TcpStream,
    coded_stream: CodedInputStream,
}

fn new_connection(s: TcpStream) -> Connection {
    Connection {
        stream: s,
        // Invalid, `s` has been transferred to `stream` above
        coded_stream: CodedInputStream::new(&mut s),
        // Invalid, `stream` is an unresolved symbol here
        coded_stream: CodedInputStream::new(&mut stream),
        // Omitting and mutating fails, since `coded_stream`
        // must have a value and there is no "0"-value.
    }
}

Am I missing something obvious? Is this generally a bad idea in Rust? Is there some other pattern for dealing with taking ownership and encapsulating a long-living object?

In Why can't I store a value and a reference to that value in the same struct? there is a similar question dealing with how to refactor internal types in order to solve the problem. Here, the CodedInputStream is outside my control, a different solution is required (I believe).

Community
  • 1
  • 1
Rawler
  • 1,480
  • 1
  • 11
  • 23
  • *[do] not attempt to put these items in the same structure together* — seems like that would apply equally well for your case? What are your results from trying to use [owning_ref](https://crates.io/crates/owning_ref)? – Shepmaster May 18 '16 at 18:05
  • It actually doesn't say _do not attempt_. It says _The easiest and most recommended solution is to not attempt_.... The problem is that it really does not apply in my case, since I cannot change the external API. It's akin of an answer of "it can't be done because that's not how rust works" which would be fine, but it's still not the same question since my question still involves an external API. – Rawler May 18 '16 at 18:28
  • `OwningRef` are close, but I realized (and I missed this in my question) that the CodedInputStream needs a mutable reference. AFAIU, OwningRef can only handle immutable values? – Rawler May 18 '16 at 18:30
  • I guess I don't see the difference. You are trying to create the struct `Connection`, correct? That means that you are able to "refactor internal types in order to solve the problem", by splitting your internal type (`Connection`) into smaller parts (the two types you don't control). The fact that the type containing the reference and the type being referred to aren't under your control is orthogonal; you still cannot put them in the same struct. – Shepmaster May 18 '16 at 18:33
  • I don't see how it is possible to "split my internal type" into smaller parts in a way that is helpful. I need to construct a value A, together with another value B(from the external API) requiring a reference to A. I want to do it such that A and B shares lifetimes (or enforce that B:s lifetime is a slim subset of A), and can be treated as a single encapsulated value by the caller. The solution proposed in the other question requires redesigning B such that referencing A is not necessary. I cannot do that, since I do not control the API of B. – Rawler May 18 '16 at 18:48
  • 1
    *The solution proposed in the other question requires redesigning B such that referencing A is not necessary* — Maybe my other answer is overly confusing then; I wasn't suggesting that you split apart the external type at all. I'm suggesting that you split apart the containing type, `Connection` in your code above. *[I want to create] a single encapsulated value* — right, you cannot do that when one of the encapsulated values references another. That's the entire point of the duplicate question. – Shepmaster May 18 '16 at 18:55
  • How `Connection` will be used? Could `coded_stream` be created on demand, like `Connection::get_coded_stream()`? – malbarbo May 19 '16 at 12:02
  • The idea would be to have `Connection` share lifetime with both the `TcpStream` and the related `coded_stream`. (To let the stream-decoder "take over" the stream). I'm now considering either just what you propose although I think will still just move the problem to the caller, or skip the use of the external API completely and implement what I need myself instead. – Rawler May 19 '16 at 12:13
  • 1
    FWIW, I don't know much about the protobuf library, but the [`InputSource`](https://github.com/stepancheg/rust-protobuf/blob/073d19aacb5299a48a25e01c575a082e1c15e9de/src/lib/stream.rs#L113-L117) type seems inelegant. I would have expected `InputSource` to be a trait that you have to satisfy and perhaps there are built-in adapters for the 3 things in the enum. That would have cleared up the whole problem. Actually, [someone else might agree that it should be changed](https://github.com/stepancheg/rust-protobuf/pull/158), although that's for output, not input. – Shepmaster May 19 '16 at 14:33

1 Answers1

1

You can't do this, because it's not safe under Rust's ownership rules.

It's a self-referential struct. There are two problems with self-referential structs:

  • All owned types can be moved to another memory address at any time, and by design that's always as simple as memcpy, without any magic. In your case this means that as soon as you return Connection, the address of stream will change, and the reference in coded_stream will become dangling.

  • The borrow checker allows struct fields to be overwritten/replaced individually. In your case it means it would allow stream to be overwritten with a new one, leaving coded_stream with use-after-free bug.

So regular borrow checking rules can't express the special relationship between the two fields that are both owned and borrowed at the same time.

There's rental crate that offers some workarounds for such situations.

Kornel
  • 97,764
  • 37
  • 219
  • 309