These are chat archives for rust-lang/rust

1st
Jan 2017
Peter Atashian
@retep998
Jan 01 2017 01:03
@matanelevi All type information is purely at compile time. At runtime it is just native code with no information on types.
Jawad Ahmad
@kahlonel
Jan 01 2017 04:22

Rust noob here :) Here's a small code function:

fn search_entry<'l>(d: &'l Vec<Person>, p: &str) -> Option<&'l str>
{
    for i in d.iter()
    {
        if i.name == p
        {
            return Some(&i.phone);
        }
    }
    return None
}

It looks for a string p in a vector of Person struct. Upon finding the name, it returns the Person's phone number as &str in an Option. Otherwise returns None. The problem is when I remove the return keyword (and of course the ;) it gives me this error:

error[E0308]: mismatched types
  --> test.rs:27:4
   |
27 |             Some(&i.phone)
   |             ^^^^^^^^^^^^^^ expected (), found enum `std::option::Option`
   |
   = note: expected type `()`
   = note:    found type `std::option::Option<&std::string::String>`

error: aborting due to previous error

Shouldn't it be exactly same as when using return keyword? I'm trying to get rid of return as most people suggest. Thanks!

David McGillicuddy
@djmcgill
Jan 01 2017 09:02
The problem is that you're inside a for-loop, which is only for side-effects. Statements inside a for-loop must have type (), which is the error that you're seeing.
David McGillicuddy
@djmcgill
Jan 01 2017 09:10
Honestly I think that using return is fine in this case. To get rid of it though, consider the iterator SkipWhile, which you can use to discard the non-matching people in front of the person you're searching for.
Then you have an iterator where the first element is equal to your target. Call .next() on it to give an Option.
I've made an example here: https://play.rust-lang.org/?gist=fc2a0294f8edf8af17c4bc7b3253a7b3&version=stable&backtrace=0
You have to deref twice for == because iter() returns an iterator of references, and SkipWhile inspects the iterator contents also by reference.
David McGillicuddy
@djmcgill
Jan 01 2017 09:19
You shouldn't have to do that on your Person type though: https://doc.rust-lang.org/book/deref-coercions.html#deref-and-method-calls
Ondřej Zahradník
@ke-si
Jan 01 2017 10:55
What is the difference between println!("{number:>width$}", number=1, width=6); with the > symbol and without it? Still does the same for me.
David McGillicuddy
@djmcgill
Jan 01 2017 11:00
"The defaults for numeric formatters is also a space but with right-alignment."
So if you specify a width for a numeric type but don't specify an alignment, it defaults to right-alignment which is what > is doing anyway.
For strings however, the default is left aligned. You can see the difference here:
    println!("{letter:>width$}", letter="a", width=6);
    println!("{letter:width$}", letter="a", width=6);
    println!("{letter:<width$}", letter="a", width=6);
Ondřej Zahradník
@ke-si
Jan 01 2017 11:08
@djmcgill Ok, thanks. Now I get how it works, but I don't see any reason for that.
David McGillicuddy
@djmcgill
Jan 01 2017 11:11
Right-alignment for numbers and left-alignment for strings is fairly typical, Excel does it too for example. It certainly predates Rust.
Consider that padding on the right with 0s will change the meaning of a number, but padding on the left won't - 100 vs 001.
That's not a full explanation, but certainly there's a historical element to it.
Ondřej Zahradník
@ke-si
Jan 01 2017 11:14
@djmcgill Ok, thanks. The example with the meaning of padded numbers with 0s makes sense.
Jonas Platte
@jplatte
Jan 01 2017 14:06
@kahlonel It might be worth noting that the Iterator trait has a lot of powerful member functions that you can use to simplify this kind of thing into a much shorter form in many cases. Your code could also be expressed as
fn search_entry<'l>(d: &'l Vec<Person>, p: &str) -> Option<&'l str>
{
    d.iter().find(|person| person.name == p).map(|person| person.phone)
}
David McGillicuddy
@djmcgill
Jan 01 2017 14:07
Ah yes, find is much better than the drop_while approach that I suggested.
David McGillicuddy
@djmcgill
Jan 01 2017 14:26
@kahlonel if you look at the source, find is implemented like you originally had, with return:
    fn find<P>(&mut self, mut predicate: P) -> Option<Self::Item> where
        Self: Sized,
        P: FnMut(&Self::Item) -> bool,
    {
        for x in self {
            if predicate(&x) { return Some(x) }
        }
        None
    }
John C F
@critiqjo
Jan 01 2017 14:29
In tokio-proto, TcpServer::threads method is a no-op on non-unix platforms. Why is that so? Socket sharing seems to be well-supported in Windows; so what's the reason for this lack of capability for creating a multi-threaded TcpServer?
David Hou
@chaosagent
Jan 01 2017 16:43
probably nobody bothered to implement it
you can put it in yourself if you want
and make a pr
(disclaimer: im not involved with tokio)
John C F
@critiqjo
Jan 01 2017 16:45
Digging a little more, it looks like the ability to reuse_port is only available in unix-systems. Then what is reuse_address? The sources seems to be doing the same thing!?
David Hou
@chaosagent
Jan 01 2017 16:49
theyre probably named differently in unix and windows at the kernel level
adding a helper function with a #[cfg(target_os = "unix)] and one with windows that calls their respective functions should work
John C F
@critiqjo
Jan 01 2017 16:54
TcpBuilder::reuse_address is a method independent of the platform. So it is supposed to work on both Linux and Windows.
reuse_address and reuse_port are apparently not equivalent in unix
John C F
@critiqjo
Jan 01 2017 17:04
Yeah, looks like it... I was just now reading rust-lang-nursery/net2-rs#12 in the related net2 issue.

It does seem reasonable to perhaps add a unifying method that sets both on Unix and the one option on Windows (in terms of "reasonably similar" behavior).

Need to check if that's what they did. Even otherwise, can't they do tcp.reuse_address(true) here to get equivalent (or "reasonably similar") behavior in Windows?

John C F
@critiqjo
Jan 01 2017 17:16
Wait a second, they do do reuse_address(true) independent of platform here. So why is there no multi-threading support for Windows? Lack of testing?
Matanel Levi
@matanelevi
Jan 01 2017 17:16

Rust's type inference is better than C++? if it does, in what aspects?

 let mut vec = Vec::new();
 vec.push(0u8);

Except this? :D (from what I know, it is impossible to do this in c++)

David McGillicuddy
@djmcgill
Jan 01 2017 17:18
That's what type inference is! Some more examples are stuff like not specifying the type of numerical literals and they're inferred from usage later.
John C F
@critiqjo
Jan 01 2017 17:21

if it does, in what aspects?

@matanelevi Have you ever specified the type of closures in Rust?! :smile: I can't even begin to imagine how it would have looked if I had to!!

Matanel Levi
@matanelevi
Jan 01 2017 17:23
@critiqjo :D, if i'm not wrong it is possible in C++ either (C14)
Peter Atashian
@retep998
Jan 01 2017 17:24
@critiqjo mio/tokio support Windows rather poorly. Partially due to lack of interest from developers, and partially due to the API design not aligning with Windows.
There's a lot of various differences in the behavior of sockets between Windows and Linux, especially when it comes to async
John C F
@critiqjo
Jan 01 2017 17:28
@retep998 I see... So simply doing reuse_address may not be enough to guarantee similar behaviors, huh?!
Peter Atashian
@retep998
Jan 01 2017 17:28
Windows has slightly different behavior from Linux when it comes to reusing ports and addresses
John C F
@critiqjo
Jan 01 2017 17:30
I was actually reading that right now... :smile:
Peter Atashian
@retep998
Jan 01 2017 17:33
that's still just a minor behavior difference. The big issue is windows async is completely different from linux async, which is mainly why mio/tokio have such poor support for Windows still
John C F
@critiqjo
Jan 01 2017 17:36
Ah, I see... The epoll mechanism vs "the other thing of windows" :smile: huh?
Hm.. That means the status quo will be maintained for much longer probably... I'm not a Windows user, but was just curious about this difference.
Peter Atashian
@retep998
Jan 01 2017 17:38
linux async is readiness based "Tell me when I can do X without blocking."
Diggory Blake
@Diggsey
Jan 01 2017 17:38
the windows mechanism is higher level, so there's a compatibility layer in mio to reimplement the lower-level readiness based api on top of it
Peter Atashian
@retep998
Jan 01 2017 17:38
windows async is completion based "Do X and tell me when it is done."
Diggory Blake
@Diggsey
Jan 01 2017 17:39
it may be beneficial to push the compatibility layer up into tokio, since tokio exposes a higher level API
Peter Atashian
@retep998
Jan 01 2017 17:40
that's partially why I really want to see someone do a futures thing directly on top of windows async instead of mio
John C F
@critiqjo
Jan 01 2017 17:52
Thanks @retep998 ... I was trying to wrap my head around how futures work by exploring top-down, starting with tokio, which was when I stumbled across "Unix-only" in docs that gave me a pause... Now, back to work...
Matanel Levi
@matanelevi
Jan 01 2017 18:17
What trait a type needs to implement so it can be transformed into slice with the syntax &var
?
Matanel Levi
@matanelevi
Jan 01 2017 18:31
@target-san thanks!
David Hou
@chaosagent
Jan 01 2017 19:03
is Vec::range O(rangelen)
Aleksey Kladov
@matklad
Jan 01 2017 19:46
@chaosagent what is Vec::range? There's no range function in ::std::Vec.
David Hou
@chaosagent
Jan 01 2017 19:46
derp
Vec::drain
Aleksey Kladov
@matklad
Jan 01 2017 19:48
I am not sure, but I think that drain itself is O(1), but if you don't consume the returned Drain iterator and just drop it, the drop would be O(rangelen)
David Hou
@chaosagent
Jan 01 2017 19:49
okay
thanks!
John C F
@critiqjo
Jan 01 2017 19:49
Yes, it is O(n) -- it could be more than len of range since it has to move all the elements the elements after the range.
Aleksey Kladov
@matklad
Jan 01 2017 19:50
@critiqjo ah, yes, thanks for clarification! But the actual work happens during the drop, isn't it?
John C F
@critiqjo
Jan 01 2017 19:55
Yeah, that's right...
But it doesn't really matter whether you consume it or not, since it will eventually be dropped and all of that will happen... so as to end the mutable borrow of the original vec.
but if you don't consume the returned Drain iterator and just drop it
Aleksey Kladov
@matklad
Jan 01 2017 20:00
Yep, drop would be O(1) only if you drain a complete suffix of the vector and has advanced the Drain iterator until the end.
Josh
@joshlemer
Jan 01 2017 20:04

I have a question. Coming from a background in Scala and wondering about Rust's apparent support for Type Classes. Found this code


// Rust
trait Testable {
    fn test(&self) -> bool
}

impl Testable for int {
    fn test(&self) -> bool {
       if *self == 0 { false }
       else { true }
    }
}

fn hello(x:int) -> bool {
   x.test()
}

I'm wondering, is it possible to have different implementations of Testable for int? Say, in some cases I want to "import" the above one, and other times I want to import a different one. In Scala this is possible so was just wondering if that works in Rust

Aleksey Kladov
@matklad
Jan 01 2017 20:06
@joshlemer no, Rust have strict coherence Rules. That is, it enforces that globally there is at most one impl for each trait, type pair.
David McGillicuddy
@djmcgill
Jan 01 2017 20:06
I don't believe it is, due to Rust's orphaning rules. Any impl of Testable for int needs to be defined either where int is defined or Testable is. Hence you can't have two implementations in different scopes.
Also coming from somebody who works with Scala for a living, I view this as a plus.
Josh
@joshlemer
Jan 01 2017 20:09
Well, one of the most common use cases for Type Classes in Scala is for things like providing an instance of a JsonFormat type class for your types, and these would of course be different for any given code base (often different in different parts of the same app), I guess that is not a common pattern, to put serialization logic in a type class in Rust?
David McGillicuddy
@djmcgill
Jan 01 2017 20:11
No, it's done in type classes too: https://docs.serde.rs/serde_json/
Aleksey Kladov
@matklad
Jan 01 2017 20:12
To provide a second serialization format, you usually defined a newtype wrapper around the type and make it serializable. Here's a real world example of this pattern from cargo: https://github.com/rust-lang/cargo/blob/master/src/cargo/ops/cargo_output_metadata.rs#L78-L84
David McGillicuddy
@djmcgill
Jan 01 2017 20:12
If you want different serialisation formats for the same time your best option is wrapper structs.
Interesting how structs are defined in a function body in that code @matklad
Josh
@joshlemer
Jan 01 2017 20:13
Ah okay, thanks @djmcgill @matklad
David McGillicuddy
@djmcgill
Jan 01 2017 20:13
I assume they'd get floated out at compile time, and it's just for scope reasons?
Aleksey Kladov
@matklad
Jan 01 2017 20:14
@djmcgill there are two question here: why structs are local to the function, and why there are structures at all?
David McGillicuddy
@djmcgill
Jan 01 2017 20:15
To derive RustcEncodable. Instead of writing a function body, you write the struct such that the derived encode is the function you want.
Makes sense I suppose.
Aleksey Kladov
@matklad
Jan 01 2017 20:16
They are local to the function because they are not needed outside the function at all. Rust allows you to define any item inside a block, though these items don't close other the environment.
And yes, you are right, they are needed to tweak encoded format. It's a nice pattern available not only in Rust: you can either defined a custom serialization format for you data, or you could defined a helper data structure which would be serialized just right by default, and then delegate the necessary implementations.
David McGillicuddy
@djmcgill
Jan 01 2017 20:18
I had no idea that was the case though. Pretty neat. I wonder if I can use that for recursive functions like:
fn foo() {
   struct FooGoArgs { ... }
   fn foo_go(args: FooGoArgs) { ... } // recursive
   let initial_args: FooGoArgs = ...
   foo_go(initial_args);
}
As I find that pattern is really common (but maybe that's because I'm from Haskell)
As at the moment I have FooGoArgs and foo_go in the same namespace as foo, even though they'll only ever be called from inside foo.
Aleksey Kladov
@matklad
Jan 01 2017 20:19
The order of definitions of items inside a block does not matter, and items can refer to one another (just like in modules). But unlike Haskell, the local items won't create a closure.
David McGillicuddy
@djmcgill
Jan 01 2017 20:20
Ah yes, that I did struggle with. In the end every value that I'd close over, I'd just put inside the FooGoArgs struct and hoped that the compiler recognised that these were always the same.
Maybe another FooGoConstants struct that gets passed as ref to all the subinvocations?
Aleksey Kladov
@matklad
Jan 01 2017 20:22
You can do something like impl FooGoArgs { fn go(&self) { ... }} inside the block as well. Useful for something like a private dfs
David McGillicuddy
@djmcgill
Jan 01 2017 20:22
an impl inside a fn inside an impl... That I did not consider
Aleksey Kladov
@matklad
Jan 01 2017 20:23
Well, you can have a whole mod inside fn...
David McGillicuddy
@djmcgill
Jan 01 2017 20:23
Memes are banned again right? Damn
A day too late!
Jawad Ahmad
@kahlonel
Jan 01 2017 22:32
Thanks @djmcgill and @jplatte :) One step closer to understanding Iterators better.