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

>>=
That's absolutely perfect then, entirely what I want. Now I just need to decide is this is a good idea or not:

Adbot
ADBOT LOVES YOU

gonadic io
Feb 16, 2011

>>=
Or even better I could make a macro that's invoked like:
code:
let zero = eight_segment_digit!(
  _ 
 | | 
 |_|
);
let two_point = eight_segment_digit!(
  _
  _|
 |_ o
);
:q:

gonadic io fucked around with this message at 00:16 on Mar 23, 2017

gonadic io
Feb 16, 2011

>>=

gonadic io posted:

Got my Arduino's ADC working in Rust! i'm p happy not going to lie

code:
const VAR_RES: Port = A2;
const ADC: *mut ADC = 0x42004000 as *mut ADC;

pub fn main() {
    unsafe {
        VAR_RES.set_pin_dir(false);
        let mut digit = create_eight_segment!(D7, D6, D3, D4, D5, D8, D9, D2);

        loop {
            let s = (*ADC).analog_read(VAR_RES) as u32;        
            digit.display( s >> 7 );
            delay(100);
        }
    }
}
https://www.youtube.com/watch?v=rE2VUSBSRB8

Now to give it a decent and safe interface as opposed to casting a u32 constant to a memory address to a pointer to a struct :q:.

gonadic io fucked around with this message at 14:07 on Apr 4, 2017

gonadic io
Feb 16, 2011

>>=
If anybody was interested in my arduino rust project, there's a new amazing guide on the best toola/libraries to use for it: http://blog.japaric.io/quickstart/

I'm busy rewriting all of my manual cludges to use this stuff.

gonadic io
Feb 16, 2011

>>=

QuietMisdreavus posted:

How long have you been out? Here's the full list of changes by version, but picking some highlights from the last several versions:


I think impl trait is my favourite of the new features, just because it makes working with unboxed closures and iterators so much easier.

gonadic io
Feb 16, 2011

>>=

Ralith posted:

I don't think that's stable yet. Is it close?

Not at all. There's still implementation bugs and lots of open design questions. However my Rust is entirely playing around with hobby projects, so I am fine with always using nightly.

gonadic io
Feb 16, 2011

>>=

Ralith posted:

I don't have any experience with Rocket, but I hardly ever find myself reaching for macros when writing Rust. First-class functions, closures, traits, and polymorphism go a long way. Which is good, because macros 1.0 really do suck for any but the most trivial uses. Custom derive (stable, demonstrated nicely by serde) and the upcoming macros 2.0 (which gives you full-powered source-to-source transforms) are a lot nicer, fortunately.

The only things that I define my own macros for are 1) early returns, like:
code:
macro_rules! try_opt(
    ($e:expr) => (match $e { Some(e) => e, None => return None })
);

macro_rules! guard(
    ($e:expr) => (if !$e { return None })
);
or 2) saving boilerplate (or abstracting in consts) like
code:
macro_rules! vert (($p:expr, $t:expr) => (
    Vertex {
        pos: [$p[0] as f32, $p[1] as f32, $p[2] as f32],
        tex_coord: [$t[0] as f32, $t[1] as f32],
    }
));
and even then 1) will go away when "try" is extended into a trait, and 2) will go away with better const fns. The only other thing that I'd write my own stuff for are when I have varargs or really want to template a function in a way that the type system can't express.

gonadic io fucked around with this message at 23:38 on Jul 2, 2017

gonadic io
Feb 16, 2011

>>=
Generalising the return type of main just got merged: https://github.com/rust-lang/rfcs/blob/master/text/1937-ques-in-main.md

gonadic io
Feb 16, 2011

>>=

xtal posted:

The cost of calling unwrap or expect once is not worth this oddity which is unique across all programming languages I've used.

If you keep defining main() { then nothing will change.

gonadic io
Feb 16, 2011

>>=
I think the need for macros will go down a bit once const_fn is stable. Certainly that's what I mostly use them for myself.

gonadic io
Feb 16, 2011

>>=
I've loved intellij's support but it's not entirely cargo free no.
Still a million times better than vs code and RLS though.

gonadic io
Feb 16, 2011

>>=
I got it working with (ignore the type annotations, they're not needed)
code:
    let d: Array1<f64> = D.dot(&(&node.a - &cam.c));

    let A: Array2<f64> = array![
        [1., 0., -cam.e[0] / cam.e[2], 0.],
        [0., 1., -cam.e[1] / cam.e[2], 0.],
        [0., 0., 1., 0.],
        [0., 0., -1. / cam.e[2], 1.],
    ];

    let f: Array1<f64> = A.dot(
        &array![d[0], d[1], d[2], 1.]
    );
i.e.
1) the array! macro in f needs its arguments to NOT be references (it's an array of floats, not an array of references to floats). Note that you copy d's members here for that to be the case.
2) the argument to dot always needs to be a reference (that's how it's defined, so that you don't need to copy or own stuff),
3) the arguments to subtract need to both be references since they both come from the borrowed function parameters. You could instead do "D.dot(&(node.a.clone() - cam.c.clone())) OR make the function own its arguments like "fn project(cam: Camera, node: Node)" if you don't use them again after passing to this function.

I think ".map(move |node| project(camera, node)).collect()" would let you make this function take its arguments by value if you don't use the camera anywhere else.

In short ndarray wants everything by reference except when constructing an array initially. This is done because ideally you want each float to be allocated once somewhere and then manipulated with read-only references. Doesn't make much difference for f64's but would with bigger types.

gonadic io
Feb 16, 2011

>>=

Dominoes posted:

Any advice on separating dependencies for WASM and binaries? I have a project that both runs locally with main.rs, and can be used as a library for WASM/JS using lib.rs. When I try to compile WASM, one or more of the dependencies that I need to run locally, but not web, prevents the WASM compilation. If I remove the crashing lib from Cargo.TOML temporarily, main.rs can't find it, which then prevents the compile. It appears that renaming main.rs, and commenting out the failing dependency works. Is there a more elegant solution?

How would you handle this?

Another option would be to split the main.rs file into a tiny new crate - you have a library that works with both and a binary that only works locally. If you make one crate that's only the library and one that's only the binary then you stop mixing up your concerns. There are two downsides: more boilerplate since you now have two crates to maintain, and your main now can't access internal stuff from the lib.

gonadic io
Feb 16, 2011

>>=

Linear Zoetrope posted:

Blugh, the RLS being borked is killing me right now. Can't use old versions because I migrated to some 1.26 features.

Use intellij with its builtin rust support, the RLS was never good. Maybe in the future it will be.

gonadic io
Feb 16, 2011

>>=
Most language features in all languages would require workarounds if they didn't exist :shrug:

gonadic io
Feb 16, 2011

>>=

Dick Nipples posted:

I mean sure.... but then you have to pass it around. Lazy_static is usually a last ditch effort thing.

It should really only be used in cases where you have a static const thing that needs to be globally available. If you’re squirreling away state in it that you later modify, you’re a bad person.

And regular global mut statics exist for this exact reason.

gonadic io
Feb 16, 2011

>>=

Beamed posted:

Anyone give the RLS a shot recently? Some of CLion's weirdness has begun getting to me.

It has not improved since the 1.0 release. I tried it when vscode's remote and docker stuff came to insider.

The biggest issue, by far, that I have with it is: https://github.com/rust-lang/rls/issues/352
i.e. code completion only works on projects which type check on things which have type annotations - there is zero type inference.

Although you're right in clion, I'm unable to use any IDE support on structs/functions defined in declarative macros even on the new macro engine. I'm not sure what's going on, I'm sure it used to work before.

E: I figured out why this was happening, jetbrains' rust plug can't handle path dependences. Bring on Rls 2.0

gonadic io fucked around with this message at 15:52 on Jul 12, 2019

gonadic io
Feb 16, 2011

>>=

mystes posted:

OK, I think I may actually hate rust now.

The problem I'm having is that with non-lexical lifetimes, if there's a variable you borrowed earlier and you think you're done with it so it's ok to try to do something else that will cause it to be borrowed again but you accidentally still have a reference to something lying around that is causing the variable to still be borrowed, the compiler doesn't say "hey, you have this other variable here causing your first variable to be borrowed," it just tells you "you already have it borrowed so you can't borrow it again dumbass" and then it's up to you to try to figure out what might be causing it to still be borrowed by looking at the lifetimes in the type signatures of everything.

Like it was totally my fault since what I was doing didn't actually make sense (I tried to use something after it wouldn't make sense to do so) so I guess the compiler was right, but I wish it could actually tell me that.

I have resorted to replacing lines of code with unimplemented!() and seeing which was invisibly holding onto a borrow, I agree. FWIW if you open an issue complaining that this error message isn't clear it'll be taken seriously.

e: and/or just replacing all usages with a clone. Life's too short to zero copy.

gonadic io
Feb 16, 2011

>>=
I made https://crates.io/crates/form and https://crates.io/crates/arduino_mkrzero when I was working on embedded stuff, but that's fallen a little down to the wayside compared to messing around with utilities for Dominions 5: a discord bot and then a few other assorted bits here and there.

I tried making a twitter app thing, that you'd log into and it'd block people for you but got a bit too turned around. I got oauth2 working, just about, but then got too paralysed about persistency and scaling
This is as far as I got: https://github.com/djmcgill/de-list-server

gonadic io fucked around with this message at 17:56 on Jul 13, 2019

gonadic io
Feb 16, 2011

>>=

ewe2 posted:

Just starting to teach myself Rust in VS code but I'm slightly confused by (I'm assuming here) RLS deciding that braces () in a macro are bad and they should be {} ?! Any ideas where to configure that behaviour?

What do you mean? I've not seen that before, can you post a snippet or two showing the behavior?

gonadic io
Feb 16, 2011

>>=

ewe2 posted:

Sure, here's some example code



I'm using vscode-rust-syntax which seems to be the only syntax highlighter that works with themes at the moment; whether its on or not doesn't affect that behaviour. So if I change all instances of () ti {}, it's happy and the code runs fine with either style.

edit: fixed it. The problem is with wayou.vscode-todo-highlight, you probably have to heavily edit its settings and its easier just to remove it.

That's pretty loving weird, not at all idiomatic - normal parens are typical there.

gonadic io
Feb 16, 2011

>>=

Ralith posted:

My comaintainer set up dependabot for Quinn, which has been decently handy for keeping on top of updates.

It's free for open source projects on github.


rjmccall posted:

I know nothing about this specific library, but to guess: the implementation type of a parser either is or is not erased. If it's erased, then at every step you have a Parser that parses a T, and that's all you know; now combinators just take an arbitrary Parser<T>, which is a concrete type which presumably has to have a concrete layout, so producing it for a particular parser implementation might require allocation. If it's not erased, then at every step you have a type P that implements a Parser that produces a T; now combinators have to be parameterized over that type P, which means that the result of applying a ton of combinators basically fully reflects the tree of combinator applications and so grows with the size of the expression. (As I understand it, in Rust this cost can be largely ameliorated with opaque trait types.)

A very similar question comes up with closures, which show up all the time in combinator parsers.

It would be extremely Rust-like for a library to not use type erasure at any stage unless you ask it to.

This is exactly it yes.

If you have a combination of 30 parsers then your type will have 30 parsers (OptP<AltP<CharP, IntP>, ...> etc) in if you don't erase at any step. The "impl trait" feature might help in some cases but it doesn't affect the underlying representation so you still get slowdowns like in this issue: https://github.com/rust-lang/rust/issues/61324

Boxing adds a runtime cost so is not the default, even if it's more convenient.

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

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.

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.

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.

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

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

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

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.

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

gonadic io
Feb 16, 2011

>>=
I don't believe stream.read can be reset half way through like that - since my understanding of how select! works is it calls poll on its futures and then if one returns Ok(value) then it executes that branch. If rx.recv returns a value then steam.read.poll is never called and any incoming bytes stay in the buffer. If stream.read.poll is called, starting the read, then in the next iteration recv finishes quickly, then stream.read.poll will either finish its future or still not be ready.

As for fixing this problem, could you get a minimal working example? There's too little context to make too much sense of it for me.

gonadic io
Feb 16, 2011

>>=
Consider something like https://docs.rs/tokio-util/0.3.1/tokio_util/codec/struct.Framed.html

gonadic io
Feb 16, 2011

>>=
Do you actually need the trait Display specifically? I often just make a write_foo method which, as a standalone function that just takes an SocketAddr as a parameter, doesn't care about orphan rules.

gonadic io
Feb 16, 2011

>>=
To make a Display wrapper is probably the "better" approach so I would still use it e.g. in a big codebase with lots of coworkers who might accidentally use the display impl. And then there's stuff like Eq, Ord, and Hash that you usually do just have to make wrappers for. And as for (De)Serialise...

gonadic io
Feb 16, 2011

>>=
Just as a quick answer, self-referential structs are only possible with the newish Pin infrastructure: https://doc.rust-lang.org/std/pin/index.html
I don't really know how it works but if you try and fail I could take a look.

It is a bit complicated though, so maybe indices is just an easier quicker bet. Also another way to do similar stuff is to not own the objs yourself but to store that somewhere else using e.g. a central memory arena or something. I believe this is the approach that e.g. the Specs entity container system (used by the Amethyst game engine) does to allow you to push and pull data while having lots of references to them in different places.

gonadic io
Feb 16, 2011

>>=
code:
use std::ptr;

pub enum Foo<'a> {
    Bar(&'a String)
}

fn main() {
    let a = String::from("blah");
    let b = Foo::Bar(&a);
    let c = Foo::Bar(&a);
    
    match (&b, &c) {
        (Foo::Bar(p1), Foo::Bar(p2)) => {
            println!("1 {}", ptr::eq(*p1,*p2))
        }
    }
    
    match (b, c) {
        (Foo::Bar(p1), Foo::Bar(p2)) => {
            println!("2 {}", ptr::eq(p1,p2))
        }
    }
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0a920fc80c23dc47935e3a96358857ae

This prints true and true. The answer is that what you're actually matching on is (Foo::Bar(ref p1), Foo::Bar(ref p2)) i.e. references to p1 and p2, which are references to a. You can't move p1 and p2 directly into your ptr::eq when you're matching on the references of b and c (but explicitly dereferencing the outer pointer copies them in).

Rust used to not insert refs into matches for you, then people got a bunch of errors and threw ref into odd places until it worked. So now rust does implicitly insert them in for you leading to situations like this.

e: you can make things even clearer by changing your printlns to e.g.
code:
println!("1 {:?},{:?} {}", *p1 as *const _, *p2 as *const _, ptr::eq(*p1,*p2))

gonadic io fucked around with this message at 20:10 on May 27, 2020

gonadic io
Feb 16, 2011

>>=
I would really recommend avoid using raw pointers for your memory arena thing. Better to use std::rc::Rc imo, way WAY less chance of your unsafe blocks being incorrect and invoking UB. (because there won't be unsafe blocks)

gonadic io fucked around with this message at 20:15 on May 27, 2020

gonadic io
Feb 16, 2011

>>=
It's a common tale. People, myself included, often feel like they're "doing it wrong" but as you say, it's rarely actually needed.

Having said that, god I love nom's zero copy stuff. Turning a flat file into a complex AST, all the strings only references into the original one. Luckily my files are small enough I can just read them into memory and don't have to figure out the streaming api.

Adbot
ADBOT LOVES YOU

gonadic io
Feb 16, 2011

>>=

Gaukler posted:

nom is real good. I’m converting a thing from using hand-rolled parsers (because “I’m only going to need to parse a couple things!”)

I’m trying to not use the macros though, have you found good resources on that? Function calls seem to be the preferred new way, but the documentation still mixes the two and most tutorials are for the macro style.

I'm just going off the latest docs.rs docs which are all updated. The functions are significantly better to use than the macros (with one caveat) but they mostly have a 1-to-1 relationship so if somebody recommends one of the macro parsers (except named!()) there's probably a function parser that's the same name and semantics. If you have a specific question just pop it here, not that I'm an expert or anything.

The main downside I've found with the function parsers is that you have to write out the type signature which, especially if you're trying to be generic over your error type, can be quite involved. I just gave up for now and monomorphised my errors which is Fine.

gonadic io fucked around with this message at 11:46 on Dec 30, 2020

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