These are chat archives for rust-lang/rust

6th
Sep 2018
Kelly Thomas Kline
@kellytk
Sep 06 2018 05:12
I don't know if it can be discerned without context, but is there a likely cause for the error "note: expected type () found type my_mod::MyProps"?
Kaspar Schiess
@kschiess
Sep 06 2018 07:40
I find that I sometimes need to read it the other way around - expected MyProps, but found (). Could that stick to your situation?
Kelly Thomas Kline
@kellytk
Sep 06 2018 08:12
I'm not certain @kschiess. Does the additional context at https://ghostbin.com/paste/vwdd3 help clarify?
Denis Lisov
@tanriol
Sep 06 2018 08:29
@kellytk The ; at the end of html! { ... }; looks incorrect
Kelly Thomas Kline
@kellytk
Sep 06 2018 08:37
@tanriol Pardon me, I mistakenly added the semicolon; it's not actually in the code
Denis Lisov
@tanriol
Sep 06 2018 08:46
Also, what's MyComponent?
trsh
@trsh
Sep 06 2018 08:59
whata heck BUG: rust-openssl lock 1 already unlocked, aborting
This bomb is dropped time to time, with segm foult
Kelly Thomas Kline
@kellytk
Sep 06 2018 09:27
@tanriol https://ghostbin.com/paste/vwdd3 updated with more context
Kelly Thomas Kline
@kellytk
Sep 06 2018 09:40
I've found the problem, thanks @tanriol and @kschiess
Kelly Thomas Kline
@kellytk
Sep 06 2018 09:46
What's the conventional file name for the file that a Rust project's shared types and constants are put into?
Zakarum
@omni-viral
Sep 06 2018 12:17
You don't need one. But if you want to - make a file per type and name file after the type name
Nonpublic stuff can go into util.rs
Ash
@ashthespy
Sep 06 2018 13:38

Got a question w.t.r closures and return types - Rust Playground

fn with_x<F: FnOnce(&mut i64)>(n: i64, f:F){
    let mut x:i64 = 5 + n;

    f(&mut x)
}

fn main() {

    let mut y = 0;
    with_x(3, |x| y = *x + 1);
    println!("{}",y);
}

Is there a way to obtain the result of the |x| *x + 1 directly without having to declare a let mut y=0 first?
i.e

let y = with_x(3, |x| *x + 1);
I know for what I am trying to achieve in this example, it can be done much simpler - but trying to apply this logic to something more complex.
Zakarum
@omni-viral
Sep 06 2018 15:18
fn with_x<F: FnOnce(i64) -> i64>(n: i64, f:F) -> i64 {
    let mut x:i64 = 5 + n;

    f(x)
}

fn main() {
    let mut y = with_x(3, |x| x + 1);
    println!("{}", y);
}
Ash
@ashthespy
Sep 06 2018 15:29
Hmm, did I understand this correct: <F: FnOnce(i64) -> i64> is the return type of |x| x + 1 i.e f(x)?
Zakarum
@omni-viral
Sep 06 2018 15:29
FnOnce(ArgumentType) -> ReturnType
Ash
@ashthespy
Sep 06 2018 15:30
Neat.
Ash
@ashthespy
Sep 06 2018 15:47
follow up question:
fn with_x<F: FnOnce(i64) -> i64>(n: i64, f:F) -> i64 {
    let mut x:i64 = 5 + n;

    f(x)
}

fn sq(x:i64) -> i64 {x * x}

fn try_sq(x:i64) -> Option<i64> { Some(x * x) }

fn main() {
    let mut y = with_x(3, sq);
    let mut z = with_x(3, |x| try_sq(x).unwrap());

    println!("y {}, z {}", y, z);
}
Is there a way to define with_x so that it can handle a return general type T
So I'd need to do something like F: FnOnce(i64) -> T ?
Zakarum
@omni-viral
Sep 06 2018 15:49
fn with_x<F: FnOnce(T) -> T>(n: T, f:F) -> T { ... }
And add bounds you need for T
Or just fn with_x<F: FnOnce(i64) -> T>(n: i64, f:F) -> T { ... } if argument type is sill i64
Ash
@ashthespy
Sep 06 2018 15:56
Rather, I want to be able to handle both x:i64 and Some(x:i64) as return types from f(x)
But this would need two definitions correct?
Zakarum
@omni-viral
Sep 06 2018 15:57
There can't be two definitions
It needs one generic definition
Ash
@ashthespy
Sep 06 2018 15:57
aha. New things to Google. :-D
Zakarum
@omni-viral
Sep 06 2018 15:57
fn with_x<F: FnOnce(i64) -> T>(n: i64, f:F) -> T {
    let x = 5 + n;
    f(x);
}
Should work for any T
Ash
@ashthespy
Sep 06 2018 15:59
i.e where T is any type?
Zakarum
@omni-viral
Sep 06 2018 15:59
Since there is no where T: ... then T can be any type
Ash
@ashthespy
Sep 06 2018 15:59
   Compiling playground v0.0.1 (file:///playground)
error[E0412]: cannot find type `T` in this scope
 --> src/main.rs:4:29
  |
4 | fn with_x<F: FnOnce(i64) -> T>(n: i64, f:F) -> T {
  |                             ^ did you mean `F`?
Zakarum
@omni-viral
Sep 06 2018 16:00
Whoops. Add T before F: FnOnce
Ash
@ashthespy
Sep 06 2018 16:00
so <T>
Zakarum
@omni-viral
Sep 06 2018 16:00
fn with_x<T, F: FnOnce(i64) -> T>
T is type-parameter. It must be introduced before usage
Simply adding it to the list of function's type parameter is enough
Ash
@ashthespy
Sep 06 2018 16:02
And if I follow the where T: I can specify what possible types T can be right?
Nice.
Zakarum
@omni-viral
Sep 06 2018 16:02
You can specify what traits T must satisfy
Like T: Clone means T must be clonnable
Same as F: FnOnce(i64) -> T means that F is a any callable that takes i64 and returns T
Ash
@ashthespy
Sep 06 2018 16:04
ah right I begin to see the sense now.
Zakarum
@omni-viral
Sep 06 2018 16:04
Without traits you can't do much with instances of the type.
You can move it (pass into function or return) and drop it (not use it, it gets dropped at end of its scope)
With traits bounds you can call methods from bound traits
Ash
@ashthespy
Sep 06 2018 16:06
Okay, let me see if I can now fix my actual problem with this new sense of understanding!
   |
55 |         self.with_selem(alsa::mixer::Selem::get_playback_volume_range);
   |              ^^^^^^^^^^
   |              |
   |              expected signature of `for<'r, 's> fn(&'r mut alsa::mixer::Selem<'s>) -> _`
   |              found signature of `for<'r> fn(&'r alsa::mixer::Selem<'_>) -> _`
Thank you though!
Zakarum
@omni-viral
Sep 06 2018 16:07
You see. with_selem expets a function that can take reference with arbitrary lifetime to the value bound to arbitrary lifetime. But the function get_playback_volume_range only work for same arbitrary lifetime at reference and type
I guess with_selem is the function you write
And I guess it has F: FnOnce(&Selem)
Ash
@ashthespy
Sep 06 2018 16:09
yes, and yes
Zakarum
@omni-viral
Sep 06 2018 16:09
After desugaring it is converted to for<'r, 's> F: FnOnce(&'r Selem<'s>)
Because you didn't write lifetimes it requries arbitrary ones
You need to write F: for<'r> FnOnce(&'r Selem<'r>)
E.g. Explicitly declare that both lifetime are same though arbitrary
Ash
@ashthespy
Sep 06 2018 16:10
the for here is for the lifetimes?
Zakarum
@omni-viral
Sep 06 2018 16:11
for<'a> is the construction to declare "arbitrary lifetime" named 'a
Ash
@ashthespy
Sep 06 2018 16:12
Hmm
Zakarum
@omni-viral
Sep 06 2018 16:13
It can be read as "for arbitrary lifetime 'r F must implement a trait named FnOnce with parameter &'r Selem<'r>"
Ash
@ashthespy
Sep 06 2018 16:14
I need to really dig into the syntax.
Zakarum
@omni-viral
Sep 06 2018 16:15
It's advanced stuff
But you can read about all of this in the "Book"
Ash
@ashthespy
Sep 06 2018 16:15
Maybe I backtrace a bit.
for the simple example that we were just looking at (with_x)
what is the practical difference b/w doing let y = with_x(3, sq); and let y = with_x(3, |z| sq(z));
Ingvar Stepanyan
@RReverser
Sep 06 2018 17:20
@ashthespy There is none as long as you don't reference outer data
But in general the latter syntax allows not just functions, but also closures which hold some data and/or references from outer context
Ash
@ashthespy
Sep 06 2018 17:21
Hmm..

Okay, let me see if I can now fix my actual problem with this new sense of understanding!

   |
55 |         self.with_selem(alsa::mixer::Selem::get_playback_volume_range);
   |              ^^^^^^^^^^
   |              |
   |              expected signature of `for<'r, 's> fn(&'r mut alsa::mixer::Selem<'s>) -> _`
   |              found signature of `for<'r> fn(&'r alsa::mixer::Selem<'_>) -> _`

But if there is no difference, how come
self.with_selem( |selem| selem.get_playback_volume_range()) works?

Ingvar Stepanyan
@RReverser
Sep 06 2018 17:23
ah
in that regard, there is difference in actual types
on function it's fixed, but on closure compiler infers it to adapt to one compatible with your argument type instead
this is one of common warts, sorry I misunderstood your question
Ash
@ashthespy
Sep 06 2018 17:25
I am not sure I completely understood that..
What does the compiler adapt here? the lifetimes?
Ingvar Stepanyan
@RReverser
Sep 06 2018 17:26
yeah, it infers them for this wrapper closure
while in case of existing function it can't just change them, they're already recorded as part of type
Ash
@ashthespy
Sep 06 2018 17:27
Right..
yes ofcourse it makes sense when you think of it right.
Ingvar Stepanyan
@RReverser
Sep 06 2018 17:28

as an analogy, this is somewhat similar to

fn f(x: u64) {}

fn main() {
    f(10);
}

working but

fn f(x: u64) {}

fn main() {
    let x: u32 = 10;
    f(x);
}

not

in one case, types are inferred to match the expectations, while in another they're used as defined
Zakarum
@omni-viral
Sep 06 2018 17:34
To be precise. The closure implements for<'r, 's> FnOnce(&'r Selem<'s>) and calls function with smallest lifetime.
So it works as adaptor. But since lifetimes are erased after borrowchecker there is no difference in generated machine code
Kelly Thomas Kline
@kellytk
Sep 06 2018 17:46
@omni-viral Why don't you think a file for shared types and constants is ever needed?
Zakarum
@omni-viral
Sep 06 2018 19:18
You can define type wherever feel appropriate. Circle references between modules are alowed
In C you would have to declare types in some header to include everywhere
Sometimes it is more intuitive to have module per type but you are not limited to this approach
Kelly Thomas Kline
@kellytk
Sep 06 2018 19:22
That's interesting, thanks @omni-viral
How would accessor syntax look for the type Box<Bridge<my_mod::MyMod>>? self.mymod.modmember gives an error of unknown field for modmember although the struct member indeed exists, and therefore I suspect the problem is in how I'm accessing through a Boxed/Bridged type
Ash
@ashthespy
Sep 06 2018 19:30

To be precise. The closure implements for<'r, 's> FnOnce(&'r Selem<'s>) and calls function with smallest lifetime.
So it works as adaptor. But since lifetimes are erased after borrowchecker there is no difference in generated machine code

Okay - in that case, will use the closure syntax :-)

Zakarum
@omni-viral
Sep 06 2018 20:02
@kellytk Box implements Deref so you can access Bridge's fields directly
Since MyMod is a generic paramter, without knowing how Bridge defined I can't say anything else about it
But if, for example, Bridge<T> has field t_field: T then you should write boxed_bridge.t_field.my_mod_member
Zakarum
@omni-viral
Sep 06 2018 20:19
Ah. It's a trait
Then you can only do boxed_bridge.send(input)
Nothing else
It is idiomatic to write Box<dyn TraitName> btw
Ichoran
@Ichoran
Sep 06 2018 20:24
Is there a widely-accepted solution to the &str vs String muddle? def f<S: Into<String>>(s: S) -> { whatever(s.into()) } works to allow f to take string literals, but it's pretty ugly. f("herring".to_string()) everywhere is worse, though.
Ryan
@rnleach
Sep 06 2018 20:26
@Ichoran Just take an &str as your function parameter, then when you pass it a string, reference it.
Zakarum
@omni-viral
Sep 06 2018 20:27
The most common case where you actually want to accept either &str or String is when you conditionally take ownership of the argument. Use Cow<str> in this case
Ichoran
@Ichoran
Sep 06 2018 20:27
@rnleach - That isn't always convenient due to lifetimes.
Zakarum
@omni-viral
Sep 06 2018 20:27
This message was deleted
Ichoran
@Ichoran
Sep 06 2018 20:28
@omni-viral - Is that transparent at the use-site?
Zakarum
@omni-viral
Sep 06 2018 20:28
No. User will have to add .into()
Ichoran
@Ichoran
Sep 06 2018 20:29
I guess that's better than .to_string() but still not ideal.
Zakarum
@omni-viral
Sep 06 2018 20:30
to_string() could be replaced with .into() cause String: From<&str>
S: Into<String> then should be your choice
Make sure to call .into() once though
Ichoran
@Ichoran
Sep 06 2018 20:31
Yeah.
Zakarum
@omni-viral
Sep 06 2018 20:31
As it can perform allocations
Or. S: Into<Cow<str>>
Ichoran
@Ichoran
Sep 06 2018 20:31
I like having this level of control when I care about performance, but I do rather wish it would all just go away when I don't care.
Hm, yeah, I should consider whether I really want Cow<str> not String.
Zakarum
@omni-viral
Sep 06 2018 20:32
S: Into<Cow<str>> will make it possible to not allocate String if you don't take ownership
And to allocate String if you do
Ryan
@rnleach
Sep 06 2018 20:34
Won't you still run into lifetime issues with the Cow<str>?
It still holds a reference (at least until you write)...
Ichoran
@Ichoran
Sep 06 2018 20:34
In my use case, I'm not sure.
Ryan
@rnleach
Sep 06 2018 20:35
I think if you want ownership, you have have the string, because all the other types are references....
You could use the &str form and then just call clone iff you need to take ownership, then otherwise just leave it as a reference.
That should minimize the need for allocations.
Ichoran
@Ichoran
Sep 06 2018 20:39
I'd have to look more carefully than I have to know whether I need ownership in the case I have right now.
The main goal is ergonomics not efficiency for this particular bit. (It's user-entered metadata, so it's tiny.)
Kelly Thomas Kline
@kellytk
Sep 06 2018 20:40
@omni-viral context: Box<Bridge<context::Worker>>, in https://github.com/DenisKolodin/yew should instead be context: Box<dyn Bridge<context::Worker>>,?
Kelly Thomas Kline
@kellytk
Sep 06 2018 20:47
FWIW, in referring to structs, I've erroneously been calling fields members
Zakarum
@omni-viral
Sep 06 2018 21:13
@kellytk yeap
Because dyn Trait more explicit and is counterpart for impl Trait
Jayesh Badwaik
@jayeshbadwaik
Sep 06 2018 21:27
Hi, I'm trying to get setup in my vim environment using Rust. I'm looking for a good code completion tool. May I have some recommendations please?
Kelly Thomas Kline
@kellytk
Sep 06 2018 23:26
Thanks @omni-viral
What is dyn an abbreviation of?
Ichoran
@Ichoran
Sep 06 2018 23:31
Dynamic
Meaning that the bindings of that trait to the underlying implementation are dynamically resolved at runtime.
(It basically creates a C++-class-like vtable to look them up in.)
Kelly Thomas Kline
@kellytk
Sep 06 2018 23:31
Oh, thanks