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

RFC: Add SocketAddr conversion trait to std::io::net and generalize socket constructors #173

Closed
wants to merge 4 commits into from

Conversation

netvl
Copy link

@netvl netvl commented Jul 21, 2014

@netvl
Copy link
Author

netvl commented Jul 21, 2014

I'd be willing to implement it if it is accepted.


```rust
pub trait AsSocketAddr {
fn as_socket_addr(&self) -> IoResult<SocketAddr>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the get_host_addresses use case, this will likely want to return IoResult<Vec<SocketAddr>> so the TcpStream::connect layer can choose between the addresses it receives.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, I think, it makes sense to add default method to the trait which returns the head of the vector, while this new method returning a vector becomes the abstract one. I'll add this to the RFC.

Added `as_socket_addr_all()` method and explained its function and
relationship with its counterpart.
@erickt
Copy link

erickt commented Jul 22, 2014

As is the wrong prefix to use here, we reserve that for zero-cost conversions. Into would be the right one to use in this case since we'd want to use move semantics. Here's the guidelines bit on this: https://github.com/rust-lang/rust-guidelines/blob/dc764ed23d1766792d66aed599d375083b898cc5/style/naming.md#conversions-rfc

@netvl
Copy link
Author

netvl commented Jul 22, 2014

@erickt, thanks for the link. But I'm not quite sure if we want to use move semantics here. to_ is probably better.

@erickt
Copy link

erickt commented Jul 22, 2014

@netvl: can you explain why?

@netvl
Copy link
Author

netvl commented Jul 22, 2014

@erickt, well, passing by reference should is usually a default, no? It is the most flexible way. And for default implementations there will be no "unwrapping" of any kind, so move semantics do not give anything of value here.

@netvl
Copy link
Author

netvl commented Jul 22, 2014

A quote from the link you provided:

Conversions prefixed as_ and into_ typically decrease abstraction, either exposing a view into the underlying representation (as) or deconstructing data into its underlying representation (into). Conversions prefixed to_, on the other hand, typically stay at the same level of abstraction but do some work to change one representation into another.

Here we obviously are not decreasing any abstraction; host names and IP address are different things, and neither is contained in another. I think to_ is the best choice; I guess I'll update the RFC.

@anasazi
Copy link

anasazi commented Jul 23, 2014

As one of the original authors of that API, I'm not sure why it was ever changed from just SocketAddr. According to rust-lang/rust@a57889a, it seemed "too low level", but that was the point.

The API was supposed to be a core set of primitives with clear costs for making TCP connections.
This is a separate concern from parsing or resolving IP addresses and thus it used SocketAddr and left the task of constructing SocketAddrs to a different API.

I'm all for the convenience of a function that auto-resolves if necessary, but such a higher-level convenience wrapper function should be part of a higher-level convenience API instead of the low-level core primitives.

That said, I would suggest the following alternative:

  • Revert the TCP and UDP APIs to use just SocketAddr
  • Add your ToSocketAddr trait for a nice conversion interface
  • (potentially) Build a more user-friendly networking library that abstract some common patterns into easy-to-use functions on top of the core primitives

Under that proposal we'd see something more like:

let mut stream = TcpStream::connect("localhost:12345".to_socket_addr()).unwrap();

which makes it clear that some additional cost (parsing + perhaps address resolution) is imposed compared to:

let mut stream = TcpStream::connect(already_prepared_addr).unwrap();

which only constructs a connection.

@alexcrichton
Copy link
Member

Thanks for taking the time to write up this RFC! Changes such as this which don't have much to do with the language and are more geared towards portions of a library don't need to go through the RFC process, however. I'm going to close this for now in light of that.

@netvl
Copy link
Author

netvl commented Sep 19, 2014

@alexcrichton, so I just can submit a PR?

@alexcrichton
Copy link
Member

Yes

bors added a commit to rust-lang/rust that referenced this pull request Nov 5, 2014
This is a follow-up to [RFC PR #173](rust-lang/rfcs#173). I was told there that changes like this don't need to go through the RFC process, so I'm submitting this directly.

This PR introduces `ToSocketAddr` trait as defined in said RFC. This trait defines a conversion from different types like `&str`, `(&str, u16)` or even `SocketAddr` to `SocketAddr`. Then this trait is used in all constructor methods for `TcpStream`, `TcpListener` and `UdpSocket`.

This unifies these constructor methods - previously they were using different types of input parameters (TCP ones used `(&str, u16)` pair while UDP ones used `SocketAddr`), which is not consistent by itself and sometimes inconvenient - for example, when the address initially is available as `SocketAddr`, you still need to convert it to string to pass it to e.g. `TcpStream`. This is very prominently demonstrated by the unit tests for TCP functionality. This PR makes working with network objects much like with `Path`, which also uses similar trait to be able to be constructed from `&[u8]`, `Vec<u8>` and other `Path`s.

This is a breaking change. If constant literals were used before, like this:
```rust
TcpStream::connect("localhost", 12345)
```
then the nicest fix is to change it to this:
```rust
TcpStream::connect("localhost:12345")
```

If variables were used before, like this:
```rust
TcpStream::connect(some_address, some_port)
```
then the arguments should be wrapped in another set of parentheses:
```rust
TcpStream::connect((some_address, some_port))
```

`UdpSocket` usages won't break because its constructor method accepted `SocketAddr` which implements `ToSocketAddr`, so `bind()` calls:
```rust
UdpSocket::bind(some_socket_addr)
```
will continue working as before.

I haven't changed `UdpStream` constructor because it is deprecated anyway.
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

Successfully merging this pull request may close these issues.

4 participants