Hacker News new | comments | show | ask | jobs | submit login

As someone who hasn't actually used Rust for anything yet, the example on the root of the site is surprising legible: https://tokio.rs/

Question for the more knowledgeable Rust folks: Does this code (the echo server example) not handle the situation where the port is already in use? What's the return type of the socket bind in that situation?

EDIT: Wow six responses in as many minutes. I think I've created a new objective measure of how popular a programming language will be!




This is actually something about the code samples in Rust docs that irks me a bit.

My view is that sample code such as this should be as idiomatic as possible and that means providing a sample demonstrating a typical real use case. So seeing "unwrap" in this context doesn't sit well with me.

In fairness, this isn't specific to Rust. I find sample code like this in many projects regardless of language. However Rust is billing itself as a safe systems programming alternative to C and C++. It would help its marketing efforts, in my opinion, by having more robust samples than the competition. And let's be honest: given the competition is C and C++, that's a low, low bar--and this comes from a guy who's about as big a fanboy of those two languages as possible.

Edit for more disclaimer: documenting Rust code is a real pleasure. The team has done a stellar job at making documentation an easy thing to do while writing the code. It's already better in most cases than many other projects I can think of.


If the bind fails, panic seems totally appropriate. Bonus points for telling me it failed for EADDRINUSE. This is head and shoulders above anything C could deliver IMO. It's a fair debate whether we should prefer the simplicity of panic over the elegance of unwinding and handling.


So, to be clear, unwrap is safe. It is never going to cause the sorts of memory issues that an uncontrolled crash is.

That is, in terms of safety, this is the same thing as explicitly handling the error and then terminating the process. Which is the only real way you're going to handle this error anyway, unless you wanted some sort of retry logic. Which you might!


I agree that in terms of safety it's equivalent. However in terms of how an actual program would be written it's...Less than optimal. I almost never simply allow a panic-like termination in a program like this, if it can be trapped, without doing something else, even if it's just to spit out a log/stdout message with a descriptive reason. Then again the plural of anecdote isn't data, and maybe I'm the oddball here.


Yeah, unwrap()s in examples should be written to use expect() (which spits a message and then panics).

But for most example programs there's no easy way to handle errors further than just using expect() unless you want there to be more error handling code than actual useful example code.


Rust n00b here (I read a lot about it but haven't written a line yet), but wouldn't `.orPanic('error message')` be a better name than `.expect('error message')`? The latter seems backwards.


It'd be or_panic, following conventions.

The idea is that I expect this to be Ok/Some, and if it's not, please use this error message.


I understand the intent, but usually the programmatic object is the subject of the sentence, the method is the verb and the parameter/argument is the grammatical object.

Here, the programmatic object is the grammatical object. That's what I meant by 'backwards'.

could_fail().or_panic_with('message') may be even better.


A lot of people don't seem to agree with me but I think even expect is too long of a name and terseness for commonly used names seems more important to me than reading like natural language.


I don't think terseness helps much here, isn't the actual error message gonna dwarf the name of the method it's passed to in almost all scenarios?


You shouldn't really be using expect much :)


It's true I use and see unwrap more.


That's not good either :|

unwrap should be confined to test and temporary code. It somewhat makes sense in example code, but expect is better for that.

There are sometimes cases when you very easily know that the unwrap will never fail and if it does something has gone very horribly wrong, in which case you might use it.

Libraries should keep usage of unwrap/expect to a minimum. Applications can be more liberal with it, but they should try to use expect or better error handling.

Not everyone perfectly follows this, sadly. But most do.


It's at least partly a matter of affordance. You could soft-deprecate .unwrap() and .expect() (emit warnings at compile time, it will be annoying but not fatal), and provide a new version of expect with a name that's more cumbersome to type (or_panic_with(...) isn't bad in that regard, but you could do even worse... or_panic_with_error_message(...) :-).


I don't neccesarily disagree. Names are hard :)


> even if it's just to spit out a log/stdout message.

This will print an error out already.

  $ ./target/debug/tokiotest
  thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error { repr: Os { code: 98, message: "Address already in use" } }', ../src/libcore/result.rs:837
Printing out a _better_ error might be helpful, though, I'll agree :) You could use expect for that, which lets you change the message in the ''s easily.

This really shows a fundamental tension though, in documentation. Is this example supposed to be demonstrating error handling? Or just get you going? Does adding more complex error handling distract from the point it's trying to teach? These are sort of open-ended questions.


Wasn't there an attempt to get a variation of main that returned result at one point(that would presumably print error and set exit code one error)? Back before "?"

If rust had that examples would be even shorter using ? Instead of try! or unwrap


FWIW there are third-party libraries which include that feature e.g. error_chain has a `quick_main` macro (working with its `error_chain!`-generated error, but I'm not sure it actually depends on it):

    quick_main!(|| -> Result<()> {
        // insert Result-returning code here
    });
Alternatively it takes a few lines to bootstrap it by hand e.g. ripgrep uses this code to bootstrap:

    fn main() {
        match Args::parse().map(Arc::new).and_then(run) {
            Ok(0) => process::exit(1),
            Ok(_) => process::exit(0),
            Err(err) => {
                eprintln!("{}", err);
                process::exit(1);
            }
        }
    }
https://github.com/BurntSushi/ripgrep/blob/master/src/main.r...


Yes, and that discussion is still ongoing. The devil, as always, is in the details.


`-> impl Trait` seems useful in that context, so that main could have a variety of appropriate return value types: `()`, `i32` (for an exit code), or `Result<T, E>` where T implements ReturnValue and E implements Error.

`quick_error` uses a trait like that to determine the exit code of `main()`, allowing it to return either () or i32.


The output of panic! seems to be useful only to the programmer, not the user. As a user, getting an error message like that would lead me to think that the application is defective, as it arguably is if it cannot provide better user experience in a completely expected error condition like a port being already in use.


The output of panic will only ever be seen by the programmer. Unwrap exists to ease prototyping and to make simple code examples. IME, the first thing you do when you take a Rust application from the prototype phase to the production phase is to grep for unwraps and insert proper error handling.


> The output of panic will only ever be seen by the programmer.

Obviously not if it's used for input errors (network failure). Crashing assertions are made for bugs, not input errors.


Please read the rest of my comment. It's not used for input errors.


This is invalid since nothing in the compiler forces you to remove the .unwrap() so it's safe to assume it will not be done before production. The whole "but this is just for prototyping" is a logical fallacy, as you know we have tons of prototypes in production ;)


I admit that I'm having a hard time seeing this criticism as anything but overblown. Finding usage of unwrap is trivially easy via textual search. Furthermore, Clippy can be used to automatically forbid usage of unwrap for your entire team ( https://github.com/Manishearth/rust-clippy/wiki#option_unwra... ). Furthermore, even when you hit a panic, it tells you which line of code in which file triggered it so that you can fix it immediately. Furthermore, the Rust community has a strong and long-entrenched proscription against libraries which unwrap rather than handle errors.

We can agree in the cynical interpretation of the laziness of programmers, but the mitigations in this case are so trivial, and the stakes so low, that focusing on unwrap as a point of contention is a poor use of energy.


Absolutely. Your users aren't likely to be seeing a server-side process like this fail, though, so in this situation, seems fine.

As I said in the post you're replying to, a nicer error message would be a good thing.


> Your users aren't likely to be seeing a server-side process like this fail, though, so in this situation, seems fine.

Whoever maintains the server and runs the service is also my user, though, in the general case.

> As I said in the post you're replying to, a nicer error message would be a good thing.

Yeah - I don't mind if unwrap panics with a dev-oriented message as it's basically an assertion, but I guess I expected expect() (no pun intended) to give a more user-friendly error. Maybe the format of the panic! output could be changed to bring the message to the front and the technical details after that.


In the world of server systems programming once an organization gets beyond a certain size it's uncommon to have programmers administering server daemons or other server applications. Those folks are indeed users.


See my sibling comment to the above. By the time your software has matured enough that it's been deployed to non-developers, unwraps have no place in the code. It's not an error-handling strategy, it's just "// TODO: Add error handling" that the compiler understands.


Example code should be exemplary.


As I said elsewhere, exemplary of what? The concept, or robust error handling? Even with the latter, it's unclear what the _right_ error handling is without knowing what you're actually doing.


Doing anything other than a crash is often sub-optimal, in my experience.

Such error handling code is usually untested, which is another way of saying 'buggy'. It almost always swallows useful information, like the backtrace. It sometimes lets program execution continue in a messed up state, causing very strange and hard to debug errors later on.

Certainly Rust makes it a lot harder to mess up error handling code than the languages I'm used to but in general I'm definitely in the 'all exceptions fatal' camp.


An exception to the exception rule IMO is a program that is managing many internal tasks at once, and the failure of one should not bring down the others. For example, a program that is coordinating many IoT devices should not fail if one of those devices cannot be contacted.


In the case of unreliable network peers, it isn't really "exceptional" that they be unreachable. It should just be represented as data.


Agreed. Looking for examples of how to handle errors, results, etc and only ever finding unwrap has frustrated me in the past, and as a noob I would have preferred fleshed out rather than shortcuts.


https://doc.rust-lang.org/stable/book/error-handling.html goes into fairly extreme level of details about this, if you're still trying to learn.

http://rust-lang.github.io/book/ch12-03-improving-error-hand... shows how to refactor a project to improve its error handling as well, for something a bit less abstract.


Sometimes a little context can be helpful.


This poses an interesting question: how hard is it in rust to enumerate all possible error sources from some code?


The mention of memory safety is kind of weird. Reading or writing to -1 in C is memory safe too. It's even sometimes done deliberately as a debug technique if you want to insert messages in your IO traces. (Wrt your post that rust is more than just safety, people will get that impression when you introduce memory safety into unrelated threads.)


While Rust is more than memory safety, it's really important that people accurately understand what safety actually means in Rust. Because then they oversell it. Common misunderstandings here:

* Rust prevents deadlocks

* You can't leak memory in Rust

* Rust prevents race conditions

* Panic is not safe

etc. In my mind, bringing up memory safety here isn't a red herring; the parent said this:

> However Rust is billing itself as a safe systems programming alternative to C and C++.

The way in which we are safer is memory safety, nothing more. And knowing that is crucial.

Rust code will have bugs. Rust code will have security vulnerabilities. Rust is not a panacea.


> The way in which we are safer is memory safety, nothing more.

I know you want to not overstate rust's claims given recent articles, but I think you're actually underselling a little here. For example, a rust `enum` make it much easier for the compiler to enforce code correctness. It's hard to go back to similar code in C or Go once you've gotten used to `match`.


Yes, I guess in my mind, "correctness" and "memory safety" are two different things. I like that Rust can help you write more correct software, but it's not nearly as strong of a guarantee as our memory safety guarantees are.


The Rust frontpage is kind of promoting these misunderstandings: https://www.rust-lang.org/en-US/index.html

> prevents segfaults

Only outside unsafe blocks.

> guarantees thread safety.

But you just said you can have deadlocks and race condition, so... perhaps change the frontpage?


> Only outside unsafe blocks.

This is a relatively uninteresting quibble: the guarantees of any language only apply outside their equivalent of unsafe blocks (e.g. Python is memory safe... until you use ctypes). Pretty much everything has a way to FFI down to interact with the machine; in Rust, it's just called `unsafe`.

(One way to look at `unsafe` is that it's a tightly integrated FFI to another language called "unsafe Rust", and it benefits from having zero performance or semantic overhead.)


Python doesn't market itself by telling it's memory-safe https://www.python.org/

If a language frontpage is inexact I'll assume the rest of the website is inexact, simple as that.


His point is that in the world of programming languages, "true memory safety" isn't really something achievable without making it impossible for your language to interact with things like native libraries or the OS. Given this, the concept of "true memory safety" isn't a useful one. In general when you say a language is memory safe, there's an implicit caveat that there may be explicit escape hatches. No language (except perhaps web-based JS) is "memory safe" by the strict definition so it's not a useful parameter to use when talking of languages. You instead use the weaker version.

Terminology matters.


Can't tell if trolling or not. Python doesn't market itself as memory-safe because it's completely expected that scripting languages are memory-safe. The only domain where memory safety isn't taken as given is in systems programming, which is why Rust's safety guarantees are a big selling point.


Not trolling. I'm just trying to convey a simple point. https://news.ycombinator.com/item?id=13378183

As it cost me 20+ downvotes to make a rational conversation with you all, I will stop interacting with the Rust community.


Firstly, the frontpage isn't promoting the misunderstandings in Steve's comment, it's promoting a different (uncommon) misunderstanding.

As I mentioned in https://news.ycombinator.com/item?id=13377957, in the world of programming languages, the term "memory safety" means something specific.

D calls itself safe on its website, but it too has the ability to escape-hatch into C/C++.

Language websites putting forth subjective claims or claims based on definitions which may be subjective is totally normal:

Go says that it "makes it easy to build simple, reliable, and efficient software", which is totally subjective.

Ruby says "It has an elegant syntax that is natural to read and easy to write.", also subjective.

Python says "lets you work quickly and integrate systems more effectively", also subjective.


I get what you are saying. What strikes me as different in the other frontpages are: the claims are made in terms vague enough to be unassailable.


You seem to be saying that because Rust is able to provide stronger guarantees, it should make weaker claims on its main page. But just because some of Rust's benefits are verifiable, unlike those of some other languages, doesn't mean the constraints on them need to be enumerated every time they're expressed. This is not "lying."

By the same token, you should be requiring that every subjective statement on the other langs' pages have the caveat "in the opinion of the $LANG developers." Obviously no one would wear that.


"thread safety" and "memory safety" are vague terms. "thread safety" is clarified there. "memory safety" has a particular meaning in that context which is true for Rust.


The guarantees Rust makes are always only outside of unsafe blocks. After all, you can call C code in unsafe blocks. :)

The page clarifies later on that the definition of thread safety in question is "threads without data races." The 16 words on the front page of the website are not the appropriate place to go into caveats and nuances.


The urge to write something clear and short should not lead to write a false fact. Or at least have asterisks.


We've tried to come up with something succinct to replace "thread safety", but it's tough.

> Only outside unsafe blocks.

This is just true of all of our guarantees. Given that the vast, vast, vast, vast majority of Rust code is safe code, I don't feel this is misleading. Do you say that Ruby doesn't prevent segfaults due to its C FFI?


I don't see an inaccurate statement on the Ruby frontpage: https://www.ruby-lang.org/fr/

edit: I'm just saying "don't lie". I shouldn't have to defend this view point and get downvotes for it.


It's not a lie. Terminology has nuance, and its meaning may change based on context.


Look, it's really really simple.

1/ It says on the tin "prevents segfaults". It does not prevent segfaults in all cases, but OK, let's debate the other claim.

2/ It also says "guarantees thread safety". What is the Wikipedia definition of thread safety? It varies, so let's take the most common "freedom from race conditions."

Steve come and say Rust can have race conditions https://news.ycombinator.com/item?id=13376485 and that it's a common misunderstanding to think it prevents race conditions. Surely it would be great if the frontpage would not promote it!

Ergo the frontpage claim is false.


I disagree with the segfaults bit (which is what you had been focusing on so far, and which is what I've been arguing against) but yeah, "guarantees thread safety" is iffy.

The wikipedia definition is pretty vague, it relies on a concept of "safe" that isn't defined there. It's acceptable to say that "data race safety" is "thread safety", though confusing. Rust's homepage does clarify what it means in the bullet points below that statement, so I wouldn't call this a lie. It may be misleading though, and this is a common misunderstanding as steve mentioned, so I submitted a PR to fix it https://github.com/rust-lang/rust-www/pull/685


Yes I think "data race freedom" when I see "thread-safety" (I despise this one term for being so vague).

In the punchline, "prevent segfaults" is framed in negative terms. If you put "provides memory safety" it will be a bit less evocative of specific pain, but will give a warm feeling.


https://github.com/rust-lang/rust-www/pull/685#issuecomment-...

Not too sure of the segfaults bit, feel free to put in your thoughts on that PR


Yes, to second Manish, I would love to find something that is accurate, succinct, and understandable. Concrete suggestions (from you or anyone else) very welcome.


My attempts so far:

Rust is a systems programming language that runs blazingly fast, guarantees memory safety and provides correct concurrency.

Rust is a systems programming language that provides uncompromised performance, convenience and security.

Rust is a systems programming language that enables unprecedented levels of performance, productivity and safety.

edit: I like (2) best


> The mention of memory safety is kind of weird.

While teaching Rust it's a recurring theme among students to believe that all crashes are created equal and that an exit from a panic must be equivalent to a segfault, despite this being significantly false. So it's common when discussing panics to novice audiences to sprinkle in "by the way, this isn't a crash, it's a controlled exit".


> Reading or writing to -1 in C is memory safe too.

Is it? It's out of bounds of any object, and therefore undefined behavior.


File descriptor -1, as in the same thing we're unwrapping in the rust code. In C, if socket() returns -1, that's actually a monad that you can pass to bind() and listen() and accept() and then check for errors. :)


FWIW it's unwrap()ing on a known-good address, which is an okay way to use unwrap(). Kind of.

It's one of the more common ways of using unwrap.

(The examples unwrap elsewhere in less known-good scenarios, I agree that we should improve there)


> My view is that sample code such as this should be as idiomatic as possible and that means providing a sample demonstrating a typical real use case. So seeing "unwrap" in this context doesn't sit well with me.

I've starting trying to use `if let Some/Ok(...) = ...` in examples for my projects rather than `unwrap` for pretty much this reason. I feel like it strikes a pretty good balance between providing a relatively terse example without the risk of implying that `unwrap/expect` is considered normal usage for the library.


That code is easy to understand. However I tried to play a little bit with tokio and my first idea was to extend the example to send all bytes 2 times back, which means I tried to avoid the copy helper method. In C/C++ or Go with a blocking socket API that would have been a 5 minute task, but with tokio I was unfortunately quite lost (didn't complete it in 2 hours). Some things that caused headaches were that buffers are consumed by the futures and how to reuse them, getting an infinite read loop running, and trying to compose different types of futures (all the time got errors that different branches have different types, which I finally silenced with using .boxed() everywhere). Maybe I should have tried to implement a custom future with a poll method instead of trying to use composition for that.

My intermediate impression is: Basics and the whole idea behind it looks very good and suits Rusts general concepts. However from a productivity point of view it yet can't match the ergonomics of Go or C# with TPL and async/await for IO tasks. But I think the rust contributors are well aware of this and working on some things (async/await like sugar and that impl trait thing which might fix my composition problems).


To be fair, you often shouldn't be using what is on the home page.

More likely, you'll be using something like this: https://tokio.rs/docs/getting-started/simple-server/.

Perhaps something like that should also be on the homepage to show that, while you can do low-level, it has already scaled up to a higher abstraction for you.


On a related note, how does the `parse()` in `let addr = "127.0.0.1:12345".parse().unwrap();` work? I am assuming that this parses a IP:port string into its components, but it looks like this is just calling a method of the basic String class. So how does it infer what `parse()` is supposed to do?


parse() is generic over it's return type (which is Result<F, F::Err> where F: FromStr) [1]. The value of which can be inferred.

The result of parse().unwrap() is of type F, which means addr is of type 'F'. afterwards when we call `let sock = TcpListener::bind(&addr, &handle).unwrap();` the compiler can infer from the signature of `TcpListener::bind`[2] what F has to be, namely `std::net::SocketAddr`

[1] https://doc.rust-lang.org/std/primitive.str.html [2] https://tokio-rs.github.io/tokio-core/tokio_core/net/struct....


Where is string to address parse implementation defined? I took a peek at SocketAddr and see a from_str function. Does that name have special significance?


The method `from_str` is from the FromStr trait. [1] The `parse` function only works on types that implement FromStr. It's "special" in that it's the conventional way to do things, but the compiler does not treat FromStr specially.

[1]: https://doc.rust-lang.org/std/str/trait.FromStr.html


It's at the very bottom of https://doc.rust-lang.org/std/net/enum.SocketAddr.html, the part that says

  impl FromStr for SocketAddr
If you look at parse https://doc.rust-lang.org/std/primitive.str.html#method.pars... it says

    fn parse<F>(&self) -> Result<F, F::Err> where F: FromStr
So any type that implements FromStr can be parse'd from a &str.


which has a link to the actual src which is here-ish and is what you might expect https://doc.rust-lang.org/src/std/up/src/libstd/net/parser.r...


If you look closely, you'll see that the from_str function is part of the implementation of the FromStr trait for the SocketAddr struct. Its this FromStr trait that the parse() method depends on.


Rust uses type inference to figure that out. Because `addr` gets passed to `TcpListener::bind` which requires a `SocketAddr`, the type inferencer knows that `addr` should be a SocketAddr and so it all works :)


You might also add that it only works because SocketAddr implements FromStr. Type inference will only determine which version of from_str to call, it won't actually do the parsing.


That type inference goes well beyond most others though.


Wow, thank you for pointing that out!

Due to the resulting thread, I feel legitimately excited to sit down and learn a new-to-me language for the first time in ages :)


I noticed that too, because it get's passed after as an argument, Rust is able to infer it's type.


The example code does not handle a failure in that situation.

The TCPListener::bind call returns a Result enum, which can have the values Ok(SomeType) or Err(SomeError). The compiler forces you to decide how you want to handle that, for the sake of brevity the example code calls unwrap() which assumes the result is Ok and will either return SomeType or else panic.


Glad to hear it, especially since this isn't really the code you'd write most often; this is a library that frameworks will use. So it can be even more ergonomic...

> Does this code

So

    let sock = TcpListener::bind(&addr, &handle).unwrap();
Here, the bind method [1] will return an error if it can't bind to the port. unwrap will cause a panic to happen.

1: https://docs.rs/tokio-core/0.1.3/tokio_core/net/struct.TcpLi...


`let sock = TcpListener::bind(&addr, &handle).unwrap();`

This line tries to bind the port and returns a `Result` type which is then unwraped, which will cause a panic when the port already is in use.


If the socket is already in use the .bind call will fail to unwrap() and the program will panic and exit.

It's generally considered bad form to use unwrap() (and by extension panic) in production code, but for examples where you just want to show something it's fine.

bind would either return a Option type or a Result type, unwrap is defined for both.

edit: Others have looked through the docs and confirmed it's a Result


This code does not handle the situation - the program will crash.

TcpListener::bind(&addr, &handle).unwrap()

Rather than raising exceptions, the paradigm in Rust is to return a Response item, which has an Ok and an Err subtype. unwrap() here assumes that the response is Ok and just gives that back the data returned, or crashes if its an error.


Nitpick: The program will print an error and abort. It won't crash. It's a "clean" shutdown.


Niptick on your nitpick: it will call the panic hook, then call into the panic runtime, which may unwind the stack or abort.

> It won't crash. It's a "clean" shutdown.

:+1:


Regarding this line:

    let addr = "127.0.0.1:12345".parse().unwrap();
Is Rust like Ruby in that devs and library authors can add methods to and redefine built-ins? And that merely including a library can alter the definition of those objects everywhere in your project's code?

I'm not trying to start a language war, but IMO that's too bad: this feature is, in my experience, the single largest source for difficult-to-detect errors that characterize the Ruby programming experience, e.g. anything that uses Rails. It also contributes to deep stack traces which are essentially useless, because the departure point from your actual code is some 20+ frames above the site of the error.


One can't add methods dynamically like Ruby nor redefine built-ins. However, one can implement traits for built-in types and the functionality in such traits is often provided as methods, giving the appearance of adding/overriding built-in functionality. Rust has the important distinction that functionality from a trait is only available when the trait is in scope, which requires an explicit import. Additionally, Rust has a static type checker, which helps lessens the risk of any features that seem implicit.

In this case, both of those methods are in the standard library.


So in this case, someone has created a trait, I guess as part of the standard library, that provides parsing of network addresses from strings?

From reading the docs, something provides a method called `FromStr` and the `str.parse` method does some magic to figure out which `FromStr` of all the registered traits is the one which we want? So then, it's not arbitrary monkey-patching, but using a defined protocol for defining and extending string parsing?


The parse method is an inherent method on the string type (i.e. not connected to a trait, and doesn't need an import), but added by the standard library, yes.

> From reading the docs, something provides a method called `FromStr` and the `str.parse` method does some magic to figure out which `FromStr` of all the registered traits is the one which we want?

The method isn't doing any magic: the language's type inference is deducing that the return type has to be a SocketAddr based on how the return value is used (it is passed to a function expecting a SocketAddr), and this forces the compiler to use the SocketAddr implementation of the FromStr trait (which has a from_str method), all at compile time.

(If the compiler deduced that the return type was something that didn't implement FromStr, i.e. no idea how to parse that value from a string, one would get a compile error.)

> So then, it's not arbitrary monkey-patching, but using a defined protocol for defining and extending string parsing?

Yes, parse is entirely driven by the FromStr trait.


> Is Rust like Ruby in that devs and library authors can add methods to and redefine built-ins?

It's closer to refinements than monkey-patching, that is, you can, but you must bring the trait into scope for it to work.

> And that merely including a library can alter the definition of those objects everywhere in your project's code?

No, based on the above rule. We don't want that.


Which line is including the trait to allow the functionality in this example? (taken from https://tokio.rs/)


In this case, it's https://github.com/rust-lang/rust/blob/b9637f79e261d0b90d23b...

So, specifically. Rust has "traits", which, for these purposes, work like Ruby modules. But. Rust has "coherence rules." That is,

  You can only implement a trait for a type if you defined either the trait, or the type, or both.
So, because libstd (well, Rust itself, but you get the idea) all three of FromStr, &str, and SocketAddr, it's allowed to do this. You, in your own package, could not define this, since you wouldn't have defined any of them.

In tokio-core, they bring SocketAddr into scope: https://docs.rs/tokio-core/0.1.3/src/tokio_core/net/tcp.rs.h...

However, I was thinking of a slightly different case when I said that, which is methods. That is, you couldn't call methods unless they've had their traits brought into scope. But parse isn't on a trait; it's defined on `str` itself. In that definition, it also has to bring FromStr into scope.

So basically, this is very muddled by the fact that this particular example uses the standard library heavily, which already implicitly brings a lot of things (like &str) into scope for you. External packages don't have that.


> So, because libstd (well, Rust itself, but you get the idea) all three of FromStr, &str, and SocketAddr, it's allowed to do this. You, in your own package, could not define this, since you wouldn't have defined any of them.

It's unclear to me what you're saying here. It's true that you can't make an inherent method `parse` on str like the one being called here, it's also true that one couldn't implement FromStr for SocketAddr in an external package, but one can create a custom generic function that uses FromStr and have that generic type be SocketAddr for some call sites. Of course, if this function wanted to have the syntax of a method call on str, it would have to be defined in a trait that users then import.

> In tokio-core, they bring SocketAddr into scope: https://docs.rs/tokio-core/0.1.3/src/tokio_core/net/tcp.rs.h....

To be clear, this isn't necessary for calling methods---including inherent methods on SocketAddr---unlike the methods in a trait. The only reason to do this is that it lets one abbreviate the name: "SocketAddr" instead of "::std::net::SocketAddr".


Ah, duh, good call.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact

Search: