Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
gonadic io
Feb 16, 2011

>>=

Progressive JPEG posted:

If anyone needs to do Rust cross-builds, use cross. It has Docker as a prerequisite, but it only took 5-10 minutes to be up and running with building arm7 binaries for a raspberry pi.

Prior to this I was trying to get things working by manually adding crossbuild toolchains via rustup and they just didn't loving work.

I found cross was a good starting point, but its docker images are pretty out of date. Did a bunch of building for windows at work and had to customise it a bit

Adbot
ADBOT LOVES YOU

Progressive JPEG
Feb 19, 2003

gonadic io posted:

I found cross was a good starting point, but its docker images are pretty out of date. Did a bunch of building for windows at work and had to customise it a bit

Seems up to date for me, though I'm not on windows:

code:
$ cargo version
cargo 1.39.0-beta (4b105b6ab 2019-09-25)

$ cross version
cargo 1.39.0-beta (4b105b6ab 2019-09-25)

taqueso
Mar 8, 2004


:911:
:wookie: :thermidor: :wookie:
:dehumanize:

:pirate::hf::tinfoil:

Did they fix cross working with selinux finally? I've been running off a PR from a year ago that never seems to land. Other than that, the tool is top notch and provides the easiest cross compiles I've ever done.

Dominoes
Sep 20, 2007

Rust's made me a lazy programmer. If I hosed something up (especially while refactoring), I step through each compiler error, and bam! It works. Thought about this after reading This controversial Jonathan Blow article - he cites dynamically-typed footguns as a reason for unit testing... got me thinking that Rust's compiler and type system accomplishes some of the same things as aggressive unit-testing, without the API inertia.

taqueso
Mar 8, 2004


:911:
:wookie: :thermidor: :wookie:
:dehumanize:

:pirate::hf::tinfoil:

It is really good at "if it compiles it works" assuming you have the right logic.

spiritual bypass
Feb 19, 2008

Grimey Drawer
I've been thinking lately that Javascript is the language where I really need unit tests because I have no idea what the code on my screen actually does

Dominoes
Sep 20, 2007

rt4 posted:

I've been thinking lately that Javascript is the language where I really need unit tests because I have no idea what the code on my screen actually does
I still can't get unit tests to work in JS!

Progressive JPEG
Feb 19, 2003

Is it just me or is the whole trait imports thing a huge pita? I don't know if its just some kind of bad library structure in tokio specifically, or if this is just a common problem with Rust in general.

"Oh you want your socket object to have send() and next() calls? Well obviously you should just uhhh ...
... pull in the right version of the 'futures' library ...
... and then add 'use futures::{SinkExt, StreamExt}'! easy!"

Like I ultimately only figured out what I was missing by tracking down an existing working example and then filtering down the imports by hand until I found the right ones. And the IDE (VSCode + rust-analyzer) is still flummoxed by it even with the fix.

gonadic io
Feb 16, 2011

>>=
In my experience the error messages usually tell you which trait to pull in, was it not doing it for you?

I still use jetbrains myself, rust analyser is a whole lot better than RLS 1.0 but still not yet there IMO.

Progressive JPEG
Feb 19, 2003

Nope, just says the function doesn’t exist until I’ve found and manually added the import that provides it

repiv
Aug 13, 2009

It might be worth reporting that, the Rust team generally treats unhelpful error messages as bugs.

big black turnout
Jan 13, 2009



Fallen Rib
I've been using adventofcode.com to learn rust for the past week and while there's definitely some things I dislike (no function overloading being a big one), I'm enjoying using it pretty well. Ownership semantics are difficult but ultimately make sense. And last night I went from having never looked at multi threading in rust to a fully functional multi threaded program with ipc within an hour or two

Malcolm XML
Aug 8, 2009

I always knew it would end like this.

taqueso posted:

It is really good at "if it compiles it works" assuming you have the right logic.

Not really. The rust type system is not that powerful modulo the borrow checker. Maybe with actual dependent types (cost generics and generalizations thereof) you could express more useful logic in types.

Arcsech
Aug 5, 2008

Malcolm XML posted:

Not really. The rust type system is not that powerful modulo the borrow checker. Maybe with actual dependent types (cost generics and generalizations thereof) you could express more useful logic in types.

It is a hell of a lot better than a lot of more mainstream languages though.

And you don’t need a PhD in math to make sense of it, unlike Haskell

big black turnout
Jan 13, 2009



Fallen Rib

Arcsech posted:

It is a hell of a lot better than a lot of more mainstream languages though.

And you don’t need a PhD in math to make sense of it, unlike Haskell

I had a lot of fun learning Haskell right up until the first time I tried to find a library to do something and instead of useful documentation there were proofs

Dominoes
Sep 20, 2007

Arcsech posted:

And you don’t need a PhD in math to make sense of it, unlike Haskell
Monad: (n) a proof by example that a concept can exist, but be impossible to explain using verbal or written language.

Ralith
Jan 12, 2011

I see a ship in the harbor
I can and shall obey
But if it wasn't for your misfortune
I'd be a heavenly person today

Malcolm XML posted:

Not really. The rust type system is not that powerful modulo the borrow checker. Maybe with actual dependent types (cost generics and generalizations thereof) you could express more useful logic in types.

You can express all kinds of useful logic in Rust types for all that they don't gracefully embed arbitrary constructive propositions. Const generics are also very much not "actual" dependent types: the meat of those is the ability for a type to depend on a statically unknown, i.e. non cost, value.

Malcolm XML
Aug 8, 2009

I always knew it would end like this.

Ralith posted:

You can express all kinds of useful logic in Rust types for all that they don't gracefully embed arbitrary constructive propositions. Const generics are also very much not "actual" dependent types: the meat of those is the ability for a type to depend on a statically unknown, i.e. non cost, value.


Better than nothing and it beats standard Haskell.

That said a type system that’s too weak to express basic stuff like state machines isn’t all that useful, just an impediment. If illegal states aren’t unrepresentative there’s little point. Did higher kinded types ever get implemented?

drainpipe
May 17, 2004

AAHHHHHHH!!!!
I'm starting to learn Rust, and I have a question about using async with threads. I'm writing a program that has multiple threads, each making different http requests. I'm using the reqwest crate for this, which uses async functions. If I try to spawn a thread, I'd need something like an async closure, which is apparently still unstable. Should I just turn the threads to async tasks? There's no reason why I couldn't, but I just wanted to touch on a greater variety of stuff in Rust.

gonadic io
Feb 16, 2011

>>=

drainpipe posted:

I'm starting to learn Rust, and I have a question about using async with threads. I'm writing a program that has multiple threads, each making different http requests. I'm using the reqwest crate for this, which uses async functions. If I try to spawn a thread, I'd need something like an async closure, which is apparently still unstable. Should I just turn the threads to async tasks? There's no reason why I couldn't, but I just wanted to touch on a greater variety of stuff in Rust.

Do you specifically need closures or can you make normal async functions and then pass those?

I know rust (and futures 0.1) reasonably well but I haven't had the opportunity to spend too much time on futures 0.3 or async proper yet unfortunately.

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

drainpipe posted:

I'm starting to learn Rust, and I have a question about using async with threads. I'm writing a program that has multiple threads, each making different http requests. I'm using the reqwest crate for this, which uses async functions. If I try to spawn a thread, I'd need something like an async closure, which is apparently still unstable. Should I just turn the threads to async tasks? There's no reason why I couldn't, but I just wanted to touch on a greater variety of stuff in Rust.

what do you mean? I'm spawning threads inside async functions right now in stable rust.

is what you want to do fundamentally different than this? https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=92cb9e316200bbafa59fd23f1b09a876

fart simpson fucked around with this message at 02:29 on Feb 24, 2020

drainpipe
May 17, 2004

AAHHHHHHH!!!!

gonadic io posted:

Do you specifically need closures or can you make normal async functions and then pass those?

I know rust (and futures 0.1) reasonably well but I haven't had the opportunity to spend too much time on futures 0.3 or async proper yet unfortunately.

It looks like thread::spawn takes closures, not function pointers.

fart simpson posted:

what do you mean? I'm spawning threads inside async functions right now in stable rust.

I'm looking to spawn threads doing async things.

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

Look at the example I just linked. Does that not work?

e: ok, I think I get it

drainpipe
May 17, 2004

AAHHHHHHH!!!!
It could very well be that spawning threads to do async things is not a correct thing to do since the whole point of async is to do concurrency in one thread. But it's just weird then that I can't make http requests (at least with the reqwest crate) on different threads without using an unstable feature.

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

drainpipe posted:

It could very well be that spawning threads to do async things is not a correct thing to do since the whole point of async is to do concurrency in one thread. But it's just weird then that I can't make http requests (at least with the reqwest crate) on different threads without using an unstable feature.

I don't understand why you want to do this in the first place, but you could probably use this if you really want to: https://docs.rs/reqwest/0.10.2/reqwest/blocking/index.html

drainpipe
May 17, 2004

AAHHHHHHH!!!!
Ah thanks! There's really no reason I want to do threads over async tasks, and I'm happy to do it either way. I'm just getting a feel for the land right now, and was curious about how various things fit together.

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

drainpipe posted:

Ah thanks! There's really no reason I want to do threads over async tasks, and I'm happy to do it either way. I'm just getting a feel for the land right now, and was curious about how various things fit together.

Well, if you have some async code and you want to fit it into threads the way you're doing, I think you could wrap your async functions in
code:
block_on
and then use them in spawned threads, if you get what I mean. It would basically be converting from async/await to threaded code.

gonadic io
Feb 16, 2011

>>=
You could also just use tokio::spawn with a threaded executor and call it a day if you don't care about having exact control.

drainpipe
May 17, 2004

AAHHHHHHH!!!!
Ok, that makes sense. Also, the more I think about it, the less having threads do async stuff makes sense. If you have to communicate between threads, it's my understanding that the std::sync stuff blocks the entire thread (please let me know if I'm wrong), which would also block all async tasks on that thread.

Progressive JPEG
Feb 19, 2003

How interchangeable is Tokio with other Rust async stuff? Been using Tokio types so far but every so often I encounter something that doesnt, and it ends up being a huge pita to try integrating across them.

gonadic io
Feb 16, 2011

>>=

Progressive JPEG posted:

How interchangeable is Tokio with other Rust async stuff? Been using Tokio types so far but every so often I encounter something that doesnt, and it ends up being a huge pita to try integrating across them.

Sounds like you answered your own question tbh.
Async-std and tokio are generally incompatible (and competing) ecosystems. Both ecosystems are based on the same Future type (so that part IS compatible) but all the stuff built on top like streams and timers and stuff isn't.

So of the 3 crates you listed,
1) is incompatible with tokio,
2) is async-std itself,
3) is the futures crate which is used for both tokio and async-std

drainpipe
May 17, 2004

AAHHHHHHH!!!!
I'm continually running into a weird problem with async stuff. Here's the problem boiled down:

code:
async fn bar() -> Result<(), Box<dyn std::error::Error>> {
    Ok(())
}

async fn foo() {
}

async fn baz() {
    let x = bar().await;
    foo().await;
}

#[tokio::main]
async fn main() {
    tokio::spawn(baz());
}
I expect that this should compile fine (maybe a warning that x is not used), but it has the following error

code:
error: future cannot be sent between threads safely
   --> src/main.rs:15:5
    |
15  |     tokio::spawn(baz());
    |     ^^^^^^^^^^^^ future returned by `baz` is not `Send`
    | 
   ::: /Users/../.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.13/src/task/spawn.rs:123:21
    |
123 |         T: Future + Send + 'static,
    |                     ---- required by this bound in `tokio::task::spawn::spawn`
    |
    = help: the trait `std::marker::Send` is not implemented for `dyn std::error::Error`
note: future is not `Send` as this value is used across an await
   --> src/main.rs:10:5
    |
9   |     let x = bar().await;
    |         - has type `std::result::Result<(), std::boxed::Box<dyn std::error::Error>>`
10  |     foo().await;
    |     ^^^^^^^^^^^ await occurs here, with `x` maybe used later
11  | }
    | - `x` is later dropped here
If I replace line 9 with let _ = bar().await or let x = bar.await().ok(), then it's fine. What does this error mean?

gonadic io
Feb 16, 2011

>>=
If a type implements the trait std::marker::Send then it's safe to send between threads i.e. contains no thread local state. Your async function's error is Box<dyn std::error::Error> i.e. the only thing we know about it is that it implements std::error::Error so there's no indication that it is Send.

Tokio's spawn uses a threaded executor so any futures it executes must implement Send - a future that returns a Result<(), Box<dyn std::error::Error>> is not Send and so you get the compile error.

A few possible fixes, from easiest to hardest:
1) change your error type to Box<dyn std::error::Error + Send>. This way you inform the compiler that the errors you're producing are thread safe. This is what you should do for a little test/toy program. Here's your snippet with just that one change working.
2) putting Box<dyn std::error::Error + Send + Debug + Clone + Sync + 'static> everywhere starts to get annoying real fast so consider using a error library. Anyhow is currently the best one for executables (everything gets cast into an anyhow::Error supertype which you can log/print as needed and downcast if you really need) and ThisError is the best one for libraries (your functions return an enum that you can pattern match on easily)
3) use tokio's single threaded local executor. Probably don't do this unless you really do have a non-thread-safe error.

e: and the reason that "If I replace line 9 with let _ = bar().await or let x = bar.await().ok(), then it's fine" worked was because you were no longer propagating that non-Send error type through the Future and were instead discarding it or converting it to None.

gonadic io fucked around with this message at 02:22 on Mar 12, 2020

drainpipe
May 17, 2004

AAHHHHHHH!!!!
Ok, if I remove the foo call afterwards, it compiles fine. From reading what you've written, I'm thinking that since there is no Future to be executed afterwards, it knows that the possible non-Send error will not be propagated, so it's fine. So basically I can't have any async function return a non-Send value unless it's at the end of a block?

gonadic io
Feb 16, 2011

>>=

drainpipe posted:

Ok, if I remove the foo call afterwards, it compiles fine. From reading what you've written, I'm thinking that since there is no Future to be executed afterwards, it knows that the possible non-Send error will not be propagated, so it's fine. So basically I can't have any async function return a non-Send value unless it's at the end of a block?

code:
async fn baz() {
    let x = bar().await;
}
You're mostly right, more precisely there's no code after the .await (and the (async) function returns unit) so the error doesn't get compiled into the async function and so the fact that it's non-Send isn't a problem.

I guess my answer is: So basically you can't have any async function return a non-Send value unless it's at the end of a block and it's not returned from the block (or as long as it is otherwise discarded).

In practice like I said you're unlikely to actually have non-Send errors.

As for why the rules are slightly inconsistent and you are allowed non-Send errors as long as you never put yourself in a position where you might be able to look at it, well it's complicated but it's due to how async functions are compiled into state machines - as long as the state machine's states never have to hold a non-Send value it's fine. You can have non-Send values appear during transitions if they're not retained in the state.

gonadic io fucked around with this message at 03:17 on Mar 12, 2020

drainpipe
May 17, 2004

AAHHHHHHH!!!!
Ok, that's kinda what I imagine was happening in the background. I should probably learn more about it, but for now it seems like it's enough info for me to avoid these issues. Thanks!

drainpipe
May 17, 2004

AAHHHHHHH!!!!
I'm running into another borrow checker problem. I have a struct that owns two receivers (using Tokio still), and I'd like to use select to poll both of them and react accordingly. However, checking a receiver is a mutable borrow of self so the borrow checker isn't allowing me to do this. This seems like a pretty basic thing to want to do, but I can't see a way to sidestep this without arranging my code so that the struct does not own the receivers. Is that the only solution?

gonadic io
Feb 16, 2011

>>=

drainpipe posted:

I'm running into another borrow checker problem. I have a struct that owns two receivers (using Tokio still), and I'd like to use select to poll both of them and react accordingly. However, checking a receiver is a mutable borrow of self so the borrow checker isn't allowing me to do this. This seems like a pretty basic thing to want to do, but I can't see a way to sidestep this without arranging my code so that the struct does not own the receivers. Is that the only solution?

If you have a mutable borrow (or just by-value) to a struct you're allowed to mutably borrow each of its fields independently and simultaneously. Plus there's tokio select macro designed to do this so I'm not really sure what you mean. The rust playground has tokio if you want to try putting together a minimal example.

drainpipe
May 17, 2004

AAHHHHHHH!!!!
Oh, I see. I actually had one of them checked via a method since I wanted to do some postprocessing. Calling the method caused it to borrow the entire struct, which must be what tripped the borrow checker. A fix would then be to put it in a struct of its own. Thanks!

Adbot
ADBOT LOVES YOU

gonadic io
Feb 16, 2011

>>=

drainpipe posted:

Oh, I see. I actually had one of them checked via a method since I wanted to do some postprocessing. Calling the method caused it to borrow the entire struct, which must be what tripped the borrow checker. A fix would then be to put it in a struct of its own. Thanks!

Yes, wanting to mutably borrow only one field of a struct and then pass the rest of the struct somewhere else is a common scenario but is unfortunately impossible* to reuse the struct itself. The easiest way is to make all the grouped fields in a second struct so you have like

code:
struct MainStruct {
  field1: Butt,
  otherFields: SecondaryStruct,
}

struct SecondaryStruct {
  field2: Butt,
  ...
  field99: Butt
}
and then you can have methods take SecondaryStruct - that way you don't need to destructure every field out and pass them around individually. It's a bit of a rough corner in Rust IMO.

*: well you can start messing around with Rc and Arc and Cell and Refcell etc but that's not what I'm talking about

gonadic io fucked around with this message at 21:29 on Mar 14, 2020

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply