Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lifetime on Channel, Sftp #53

Closed
beamspease opened this issue Jan 31, 2017 · 7 comments
Closed

Lifetime on Channel, Sftp #53

beamspease opened this issue Jan 31, 2017 · 7 comments

Comments

@beamspease
Copy link

The explicit lifetime needed on Channel and Sftp creates a lot of problems.

First, I asked about creating a struct that holds the socket, the session, and the channels and was told that there isn't any good way to do self-referencing structs in #rust-beginners.

Second, it becomes viral and infects additional traits and generics, leading to errors such as E0207.
rust-lang/rust#25041

From my reading, it sounds like the recommended solution is Rc or similar.

This one design decision is making it look like the project I'm working on may be infeasible to do in Rust. I'm refactoring code to try and dodge the issue, but it's a real pain.

@alexcrichton
Copy link
Owner

Yeah the API here is kinda unfortunate but it reflects what libssh2 is doing under the hood. (it internally isn't using reference counting).

It should be safe, however, to throw a Session into an Rc, transmute its lifetime to 'static, and then pair the Rc with a Channel or other sub-resource. That way the sub-resource has the 'static lifetime and persists a reference to the Session with it.

@beamspease
Copy link
Author

Thanks. I hammered on it all night and, with some help from a few people in #rust-beginners, was able to get past the blockage by adding another lifetime.

I worked around not being able to set the fields on the struct simultaneously by making the channels optional and setting them after the effect. It adds a bit of boilerplate but nothing too terrible.

I haven't used transmute yet, but that sounds like it may be a more elegant solution.

@alexcrichton
Copy link
Owner

Yeah working around this definitely isn't trivial, but I'm thinking of something like:

struct OwnedChannel {
    inner: Channel<'static>,
    _session: Rc<Session>,
}

(or along those lines at least)

@beamspease
Copy link
Author

How I've worked around this so far is by creating a connector struct that exists concurrently with the struct that represents the actual connection. There's a connect() function that creates and returns the channels created from the SSH session and stores them in the object representing the connection handle.

I'm still not terribly happy with it since what I've read suggests that the Connection should take ownership of the handle. I did try the approach you suggested, but I got bogged down in some aspect of it that I can't remember now. However I tackled this in the first few weeks of learning Rust, on what turned out to be the most lifetime-complex part of what I've written so far, so the results might be different if I took a stab at it.

My solution and some other cases of storing child objects derived from some parent object has guided me towards feeling that self-referential structs are important to allow a container object to take ownership of a source object when it's also storing derived objects. Short of the approach above (which would incur overhead, albeit very slight, which could become more significant in large numbers of transient objects) or unsafe{} I'm not sure how you would implement something like that using lifetimes. Supposedly there are crates, but it's a counter-intuitive situation to need a third-party library for that. Though I digress a bit.

@alexcrichton
Copy link
Owner

Yeah unfortunately I think a perfect solution here isn't great (we may not have enough language features), but I think we can nonetheless still improve the status quo! If I were to redo the API I think I'd do away with all the borrows. Especially as an async-capable library I think working with borrows is just too onerous and not worth it in the long run.

If you're interested in upstreaming that I'd be ok releasing a new major version as well!

@beamspease
Copy link
Author

beamspease commented Apr 26, 2017 via email

wez added a commit to wez/ssh2-rs that referenced this issue Jul 24, 2019
Instead the internal session is kept alive via Rc

Refs: alexcrichton#53
wez added a commit to wez/ssh2-rs that referenced this issue Jul 24, 2019
Instead the internal session is kept alive via Rc

Refs: alexcrichton#53
wez added a commit to wez/ssh2-rs that referenced this issue Jul 29, 2019
Instead the internal session is kept alive via Rc

Refs: alexcrichton#53
wez added a commit to wez/ssh2-rs that referenced this issue Jul 29, 2019
Instead the internal session is kept alive via Rc

Refs: alexcrichton#53
wez added a commit that referenced this issue Jul 29, 2019
Instead the internal session is kept alive via Rc

Refs: #53
@wez
Copy link
Collaborator

wez commented Jul 29, 2019

I've made some changes to this on master that remove the borrows; these will be in the next release of ssh2

@wez wez closed this as completed Jul 29, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants