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

>>=
Certainly looks functional. Some advice to improve it:
- For one you'll certainly want to run the linter clippy on your code.
- You don't need the `Option::`
- This one is a performance optimisation but you don't need three hashmaps - you could leave the valid keys behind in the original serde_json hashmap and just return that at the end. E: You'll want to remove unknown values ofc. Maybe better to move values from the original hashmap than copy them. Check out the `entry` method.
- You seem to have forgotten to actually return the 400 in the error case
- serde_json has from_slice which avoids the String step

gonadic io fucked around with this message at 21:02 on Sep 26, 2023

Adbot
ADBOT LOVES YOU

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself

gonadic io posted:

Certainly looks functional. Some advice to improve it:
- serde_json has from_slice which avoids the String step

thanks for the help! this was the one that was specifically tripping me up though because from_slice has no .get() method. Is there a way to check for the existance of a key without panicking that I was just not finding in my google searches?

as far as I can tell, I can only do something like

Rust code:
serde_json::from_slice(json).username
serde_json::from_slice(json).password
serde_json::from_slice(json).blahblah // panic

teen phone cutie fucked around with this message at 22:12 on Sep 26, 2023

gonadic io
Feb 16, 2011

>>=
Look at the type signatures of the from_str and from_slice methods. Both can parse into anything that implements the Deserialize trait. If you then click on that and scroll down, you'll see that hashmap is one of those things.
It might be that you need a type annotation like you already have.

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself

gonadic io posted:

Look at the type signatures of the from_str and from_slice methods. Both can parse into anything that implements the Deserialize trait. If you then click on that and scroll down, you'll see that hashmap is one of those things.
It might be that you need a type annotation like you already have.

oh huh i told the method to return a hashmap and.....it just worked. This seems like magic.

well thank you again. do you know of any docs that explains this kind of type signature conversion to me like i am a child? Python and Typescript are my only background, and some of this stuff is really hard to wrap my head around. Or rather, is this just something special serde_json is doing that isn't typical?

e: we're in business now


teen phone cutie fucked around with this message at 22:45 on Sep 26, 2023

Dijkstracula
Mar 18, 2003

You can't spell 'vector field' without me, Professor!

Reading about the From and Into traits is where I would start.

gonadic io
Feb 16, 2011

>>=

Dijkstracula posted:

Reading about the From and Into traits is where I would start.

While these are good they don't specifically answer the question.

Specifically this is the method signature:
https://docs.rs/serde_json/latest/serde_json/fn.from_slice.html

It returns a type parameter T, where T is a type that implements the trait Deserialize. If you then look at the docs of that trait it lists all the types that implement it, that you can parse into. Also if you used serde_derive on a struct/enum, those types would implement it too.

You absolutely would benefit a lot from reading about traits in general, but the shortest version is that they're a collection of methods. If a type implements a trait, you can call one of those methods on that type. Deserialize is a pretty complicated trait, and not a good first one to investigate (From and Into are much better there).

It is a bit magical to have a polymorphic return signature that's freeform (subject to the trait) like that and not too many things do it. The other big one you'll know is the collect method.

gonadic io fucked around with this message at 23:02 on Sep 26, 2023

Dijkstracula
Mar 18, 2003

You can't spell 'vector field' without me, Professor!

gonadic io posted:

While these are good they don't specifically answer the question.
Sure, I suggested From and Into because they’re the simplest example of “how does just assigning a thing to a seemingly-unrelated type turn it magically into that type” versus “how does serde do it in this particular case”, but I can see how that’d actually confuse the issue here :shobon:

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself
thanks so much for the direction on this really! I joined 2 different discords this week asking these questions and got a bunch of answers ranging from "I would just have all the validation live in the client" or "i wouldn't aggregate errors at all and just return a single 'form invalid' error" and the best one, "just use a library for this" and other completely useless feedback that isn't teaching me how to use rust

Dijkstracula
Mar 18, 2003

You can't spell 'vector field' without me, Professor!

yeah I used to hang out in the discord a bunch but it seemed like mostly grouchy old-timers (which I don't necessarily fault them for, if I was answering basic questions over and over again I might turn that way too), but agreed that it isn't often optimal for learning

Dijkstracula
Mar 18, 2003

You can't spell 'vector field' without me, Professor!

Silly macro question that I can't find a good answer for:

I'm building a compiler for a research language in order to write some Rust in anger, and I have a unit test that looks like this, but unfortunately doesn't parse:

Rust code:
        let sort = ...
        ...
        assert!(matches!(
            sort,
            IvySort::Class(Class { 
                parent: None, 
                fields: [("x".into(), IvySort::Number), ("y".into(), IvySort::Number)].into()
                ..
            }))
        );
So, I'm forced to project out the enum discriminant and have a separate assert for the contents of the `fields` field without using matches!:

Rust code:

        let sort = match sort {
            IvySort::Class(cls) => cls,
            _ => panic!(),
        };

        assert!(matches!(
            sort,
            Class { 
                parent: None, 
                ..
            })
        );
        assert_eq!(
            sort.fields,
            [("x".into(), IvySort::Number), ("y".into(), IvySort::Number)].into()
        );
I don't particularly care about having to write it this way, but, is there a better way?

gonadic io
Feb 16, 2011

>>=
It being nested is a bit annoying. I've made fake little structs like
code:
TestSubClass { parent: Option<Whatever>,  fields: Vec<(String, IvySort)> }
before, slap an Into impl in there and you can use it for your tests. But it being inside the enum variant means you have to still have an explicit match in there or at least make some test function that contains one. Also you'll want to use the `fail` macro instead of just `panic` in your tests.

Dijkstracula
Mar 18, 2003

You can't spell 'vector field' without me, Professor!

Thanks!

gonadic io posted:

But it being inside the enum variant means you have to still have an explicit match in there
Sorry, do you mean that I can nested matches! calls? I'm fiddling with this but not pulling it off.

gonadic io posted:

Also you'll want to use the `fail` macro instead of just `panic` in your tests.
:nice: I did wonder if there was a more testy-way of doing this.

Moonlit Knight
Nov 26, 2018
Not that it makes a huge difference, but I think the "let sort" part may benefit from let-else.

Also, in the second block you can just
code:
assert!(sort.parent.is_none())
.

Moonlit Knight fucked around with this message at 00:38 on Sep 30, 2023

Dijkstracula
Mar 18, 2003

You can't spell 'vector field' without me, Professor!

Yeah I elided a bunch of extra fields to keep the example code tidy, but you're not wrong that I forgot about let-else

gonadic io
Feb 16, 2011

>>=

Dijkstracula posted:

Thanks!

Sorry, do you mean that I can nested matches! calls? I'm fiddling with this but not pulling it off.

No, that you can't use TestSubClass or something like it until you already do the match into the IvySort::Class variant. I would define a get_class method on IvySort that returns an Option<Class> tbh, then in your test you call expect on that , and can then call matches!, that's how the syn library does it.

Music Theory
Aug 7, 2013

Avatar by Garden Walker

gonadic io posted:

It being nested is a bit annoying. I've made fake little structs like
code:
TestSubClass { parent: Option<Whatever>,  fields: Vec<(String, IvySort)> }
before, slap an Into impl in there and you can use it for your tests. But it being inside the enum variant means you have to still have an explicit match in there or at least make some test function that contains one. Also you'll want to use the `fail` macro instead of just `panic` in your tests.

IIRC you can do something like let Some(foo) = bar else { todo!() }; for slightly clearer code than a whole match.

notwithoutmyanus
Mar 17, 2009
I'm trying to wrap my head around this:

code:
fn main() {
    let mut a: [i32; 6] = [10, 20, 30, 40, 50, 60];
    println!("a: {a:?}");

    let s: &[i32] = &a[2..4];

    println!("s: {s:?}");
}


That part that throws me off is: let s: &[i32] = &a[2..4]; . Wouldn't specifying that you're borrowing cells 2-4 print out 20 30 and 40, not just 30 and 40? I know I'm wrong/missing something here, so why am I wrong?

pseudorandom name
May 6, 2007

notwithoutmyanus posted:

I'm trying to wrap my head around this:

code:
fn main() {
    let mut a: [i32; 6] = [10, 20, 30, 40, 50, 60];
    println!("a: {a:?}");

    let s: &[i32] = &a[2..4];

    println!("s: {s:?}");
}


That part that throws me off is: let s: &[i32] = &a[2..4]; . Wouldn't specifying that you're borrowing cells 2-4 print out 20 30 and 40, not just 30 and 40? I know I'm wrong/missing something here, so why am I wrong?

Arrays are zero-based, the element at offset 2 is the third element in the array.

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
Also, a .. range excludes its endpoint.

notwithoutmyanus
Mar 17, 2009

Ralith posted:

Also, a .. range excludes its endpoint.

Ah! Thank you! This is the other side of what I was wondering. I did get the order, but this was what I missed. The other post made sense and I wasn't clear on that I understood reference to 2 = 3rd cell in the array, it was just...this, was what I was trying to understand.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
For a range that includes both endpoints, you have ..= (which looks a bit strange but is pretty clear in practice).

The March Hare
Oct 15, 2006

Je rêve d'un
Wayne's World 3
Buglord
Hi, lifelong Python guy here. Just getting into Rust and have some questions about how people handle things in more stringent type systems.

I'm making an HTTP request to an endpoint whose 200 response payload is stable - I do not anticipate its shape changing at all.

In Python, I might set up something like a struct mapping to all of the keys in the JSON response from this endpoint. That would be helpful for code completion and documentation for other devs on the project.

Using ureq in Rust they suggest doing just that - I can do something like:

code:
#[derive(Deserialize)]
struct DummyExpectedResponse {
    field1: String,
    field2: String,
    urMomsPhoneNumber: String,
    field3: String
}

let resp: DummyExpectedResponse = ureq::post...
This is gravy, except I may not need to use your mother's phone number in this program which leaves me with a linter warning about unused fields in the struct. Python linters, in my experience, would not complain about this.

My understanding is I can either prepend an underscore on fields that are unused, and then remove that underscore later when I use the fields, or I can slap an `#[allow(dead_code)]` at the top of the struct to tell the linter to chill out.

My brain tells me that this thing is useful as documentation and an interface. For that reason, I'd rather leave it unmolested - the underscores feel like they would be annoying to remove later and unlikely to clarify any question other than "which fields on this struct are in use in this program right now" - which doesn't seem like a very useful question to have an answer to.

For those reasons, I'm erring on just slapping `#[allow(dead_code)]` on it and calling a day. But I'm curious to hear how other people feel about this &or if there's some other way I could be solving this issue.

Love Stole the Day
Nov 4, 2012
Please give me free quality professional advice so I can be a baby about it and insult you
To translate what we would do in Java world, the way to solve this problem is to move all of these empty dataclass structs into their own separate Crate package, call it an "interface", and then tell that entire crate to ignore that, by doing `#![allow(dead_code)]` (with the exclamation point) at the crate's root file. That way, your project will have the important stuff, and all those empty dataclass structs will sit alone in their own special place... which you access by specifying it as a dependency in your Cargo.toml file

gonadic io
Feb 16, 2011

>>=
One of the things people from dynamic types don't really appreciate about static types at first is how easy it is to do the kind of refactoring like later adding a field. There's not really a need to leave stuff like that lying around in case you need it when it's easy to just add it when you need it. I personally would comment it out and leave little note for when I came back to it.

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

If you're using serde_json or similar, you can just leave out the fields you're not interested in. Extra fields aren't an error, and you don't need to spend time parsing them.

gonadic io
Feb 16, 2011

>>=
Also if you're using an ide, which you should be, removing the underscores is like 2 clicks. The two main ones are vscode with rust analyser plug-in, or jetbrains intellij with rust plugin.

The March Hare
Oct 15, 2006

Je rêve d'un
Wayne's World 3
Buglord
I appreciate knowing I can just leave them off entirely with serde_json (that is what I'm using, I think) but I think the real driving force behind my desire to leave them in the struct is as documentation of the shape of the response from the API.

If I remove them from the struct then I'll have to go inspect the response later if I want one of the other fields that I'm not using right now. Or, worse, I may think that everything in the struct _is the entirety_ of the shape of the response from the API, and not realize that I could also be accessing your mother's phone number, which would be a disaster for my love life.

What I'm less worried about is the "difficulty" of the refactor in terms of finger mechanics.

Moving them into their own little crate seems reasonable enough I guess, though it feels a little extra for just this little toy example. Feels like a solution I'd reach to if I were mapping a large part of a larger API or something though.

The March Hare fucked around with this message at 23:27 on Nov 3, 2023

Ranzear
Jul 25, 2013

gonadic io posted:

I personally would comment it out and leave little note for when I came back to it.

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

The March Hare posted:

I appreciate knowing I can just leave them off entirely with serde_json (that is what I'm using, I think) but I think the real driving force behind my desire to leave them in the struct is as documentation of the shape of the response from the API.

It's not authoritative, so I wouldn't want to trust it as docs anyway. Tag the whole structure as `non_exhaustive` maybe.

The March Hare
Oct 15, 2006

Je rêve d'un
Wayne's World 3
Buglord
Cool, thanks for all the replies everyone - all makes sense.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I have a colleague with a design and language situation we're trying to figure out how to solve in Rust. He's porting a Python project that is making heavy use of mixins to handle the multiple inheritance he was dealing with. I don't fully understand all the details myself, but he has state that has to carry along with methods so he can't just go fully virtual. I don't know how to replicate this in Rust because mixins are kind of nerfed in a statically typed language in the first place, and we don't have inheritance to use in the second place.

We figure there is probably some macro voodoo we could employ here, but we're just little children and wouldn't know how to even approach that kind of thing. The mixins aren't a truly dynamic situation; they're specified at-once and then the definition is effectively "finished" so I think some kind of macro in one spot could finish the job. It's just figuring out how to fill in a struct while employing various functions in traits simultaneously. I got the notion in more recent versions of Rust that something this wild is actually possible, but that's it.

Uhh, I guess a plan B would be to go with a pure compositional approach with entities and components, but then we're putting a lot more overhead on runtime lookups for something that isn't dynamic after its initial definition.

Plan C would just be a lot of duplicate typing (copy paste) that will doom us when one of the base, common things changes some day.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
Isn't a "mixin" essentially just:

code:

struct MyType {
  //...other fields
  mixin_delegate: MixinDelegate,
}

impl MixinTrait for MyType {
  fn mixin_func(self: &Self) {
    self.mixin_delegate.mixin_func();
  }
}
?

Are you looking to do that delegation with less typing or something?

You certainly could write a macro for that easily enough. In fact it looks like someone already did, though I've never used it so can't speak to whether it's any good.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I think every mixin is coming with additional fields in the struct too. Otherwise, just having extension methods or whatever Rust calls it would be pretty obvious. I'm going to check out that link though and you reminded me to actually ask my colleague to verify that problem with the additional state.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
That mixin crate might just be it. I had done a search originally a few months ago when we were first kicking this around, but didn't find anything. I think I just had a lack of faith in there really being something so I didn't really look. That's on me.

Ihmemies
Oct 6, 2012

Fffuuu.

I need to implement a

Rust code:
fn josephus(mut n:usize, mut k:usize) -> String {}
Where 1 <= n <= 2*10^5.

I am in deep crap. Do I have some other options than a circular linked list, implemented with raw pointers? I don't know how to do it but I can learn, can't be much tougher than in ANSI C.

Dijkstracula
Mar 18, 2003

You can't spell 'vector field' without me, Professor!

I don’t really remember precisely what the Josephus problem is, but what limits you from using a VecDeque?

There’s also a linked list implementation in the standard library too.

Dijkstracula fucked around with this message at 18:55 on Dec 11, 2023

Ihmemies
Oct 6, 2012

Dijkstracula posted:

I don’t really remember precisely what the Josephus problem is, but what limits you from using a VecDeque?

There’s also a linked list implementation in the standard library too.

Consider a game where there are n children (numbered 1,2,..,n) in a circle. During the game, every kth child is removed from the circle until there are no children left. In which order will the children be removed?

And I need to return a list of removed persons, in the removal order. My futile attempt was too slow:

Rust code:
fn josephus_slow(mut n:usize, mut k:usize) -> String {
    let mut people: Vec<usize> = (1..n + 1).collect();
    let mut idx = 0;
    let mut sequence: Vec<usize> = vec![];

    while !people.is_empty() {
        idx = (idx + k - 1) % people.len();
        // Probably WAY TOO SLOW.. remove runs O(n)
        sequence.push(people.remove(idx));
    }
    
    sequence
        .iter()
        .map(|&n| n.to_string())
        .collect::<Vec<String>>()
        .join(" ")
}
How would a vecdeque help? Rust's Linked list is Doubly-linked and really supports removing only elements from either end.

fritz
Jul 26, 2003

A "pointer" can be something that indexes into an array. You could have a data structure that contains a next_index and prev_index and update those as you iterate thru.

Ihmemies
Oct 6, 2012

VecDeque helped somewhat, but not still enough. Maybe smaller integers? I finally actually timed what takes so long, and I realized printing!

Rust code:
fn josephus(mut n:i32, mut k:i32) {
    let mut people: VecDeque<i32> = (1..n + 1).collect();
    let mut idx = 0;

    let stdout = io::stdout();
    let lock = stdout.lock();
    let mut w = io::BufWriter::new(lock);

    while !people.is_empty() {
        idx = (idx + k - 1) % people.len() as i32;
        write!(w, "{} ", people.remove(idx as usize).unwrap()).unwrap();
    }
    w.flush().unwrap();
}
Writing data straight out like that is way faster than using either print or println or storing the values anywhere.

Pointer usage will be saved for another day. Thanks...

Adbot
ADBOT LOVES YOU

Moonlit Knight
Nov 26, 2018
Why k - 1? For k = 1 every child should be removed in order, but in your code, idx would be constant, right?

Edit: oh, got it. You need to decrement the index because the queue has one fewer element.

Moonlit Knight fucked around with this message at 23:14 on Dec 11, 2023

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