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

>>=

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

Adbot
ADBOT LOVES YOU

Pie Colony
Dec 8, 2006
I AM SUCH A FUCKUP THAT I CAN'T EVEN POST IN AN E/N THREAD I STARTED
Hey all, I'm quite new to Rust, and I'm basically trying to code up the following:

Rust code:
struct NetworkManager {
  connection: Option<Peer>,
}

impl NetworkManager {
  fn connect() -> Peer { ... }

  pub fn send(&mut self, data: Packet) {
    if self.connection.is_none() {
       std::mem::replace(&mut self.connection, Some(NetworkManager::connect()));
    }

    match &mut self.connection {
       Some(conn) => { conn.send_packet(data); }
       None => { }
    }
  }
}
Two questions--

1) Is this the idiomatic way to model initialized/uninitialized data in Rust? The connection may be reset in other internal functions, but re-connecting should be invisible to the caller. Also, I think I'm relying on Rust's optimization that makes None a null pointer, so I don't have to worry about cleaning anything up before calling std::mem::replace. Is that accurate?

2) The exact Peer class is this one: https://docs.rs/enet/0.2.3/enet/struct.Peer.html -- it requires a lifetime to use, and I get a clean build if I mark NetworkManager as having the same lifetime as Peer in the struct definition. However I think this will cause me headaches later, since NetworkManager is really capable of out-living any one Peer. What's the best way to denote this kind of relationship?

gonadic io
Feb 16, 2011

>>=

Pie Colony posted:

Hey all, I'm quite new to Rust, and I'm basically trying to code up the following:

Rust code:
struct NetworkManager {
  connection: Option<Peer>,
}

impl NetworkManager {
  fn connect() -> Peer { ... }

  pub fn send(&mut self, data: Packet) {
    if self.connection.is_none() {
       std::mem::replace(&mut self.connection, Some(NetworkManager::connect()));
    }

    match &mut self.connection {
       Some(conn) => { conn.send_packet(data); }
       None => { }
    }
  }
}
Two questions--

1) Is this the idiomatic way to model initialized/uninitialized data in Rust? The connection may be reset in other internal functions, but re-connecting should be invisible to the caller. Also, I think I'm relying on Rust's optimization that makes None a null pointer, so I don't have to worry about cleaning anything up before calling std::mem::replace. Is that accurate?

2) The exact Peer class is this one: https://docs.rs/enet/0.2.3/enet/struct.Peer.html -- it requires a lifetime to use, and I get a clean build if I mark NetworkManager as having the same lifetime as Peer in the struct definition. However I think this will cause me headaches later, since NetworkManager is really capable of out-living any one Peer. What's the best way to denote this kind of relationship?

1) I'd use Option::replace or even just `self.connect = Some(...)`. In general in rust I find myself dropping into std::mem very rarely.
2) Uh I think you're going to have trouble there. The docs say

quote:

The lifetime of these instances is not really clear from the ENet documentation. Therefore, Peers are always borrowed, and can not really be stored anywhere.
and from searching through the docs the only way you can get one is from this function on a Host:
code:
    /// Returns an iterator over all peers connected to this `Host`.
    pub fn peers(&'_ mut self) -> impl Iterator<Item = Peer<'_, T>> {
        let raw_peers =
            unsafe { std::slice::from_raw_parts_mut((*self.inner).peers, (*self.inner).peerCount) };

        raw_peers.into_iter().map(|rp| Peer::new(rp))
    }
Note that the lifetimes are hidden in the docs, but shown in the code. Thanks elision. Anyway it doesn't really make sense to be able to store one of these, you'll need to just store references to them. And to store a reference, you'll need to make sure that the Host (whose lifetime the Peer inherits) will outlive wherever you store the Peer reference.
If you must store a (reference to a) Peer, you'll certainly need to store the Host in the NetworkManager too if you're not already storing it somewhere further up.
I don't really know what kind of behaviour this library provides or you want out of it so I can't give more specific advice.

Gaukler
Oct 9, 2012


You could model the state transition with two different types so you don’t have to litter Option checks everywhere. A DisconnectedNetworkManager that holds connection options and can connect() to return a ConnectedNetworkManager that has a Peer (not Option<Peer>) field and can send().

This way, your ConnectedNetworkManager lifetime will also line up with your Peer lifetime as well.

This might not make sense if you were expecting it to do a bunch of state transitions and you really do need to keep checking that you’re connected, though.

Pie Colony
Dec 8, 2006
I AM SUCH A FUCKUP THAT I CAN'T EVEN POST IN AN E/N THREAD I STARTED
Thanks for the replies. I did get it working, but still struggling a bit with the lifetimes of Peers.

- Peer (the packet queue writer) is only obtainable from a &mut Host (https://docs.rs/enet/0.2.3/enet/struct.Host.html)
- Sending queued packets and receiving response packets is only possible from a &mut Host (https://docs.rs/enet/0.2.3/enet/struct.Host.html#method.service)
- Which means, in a single scope (say, a game loop), I can only queue a packet if I receive a packet first

code:
let peer = host.connect(..)?;

loop {
    match host.service(0)? {
        Some(Event { .. from_peer .. }) => { from_peer.send_packet(..); }
        None => { /* impossible to call `peer.send_packet` as I'd be borrowing host twice */ }
    }
}
Am I missing something obvious here, or is this just how the library works?

Pie Colony fucked around with this message at 19:07 on Jan 9, 2021

gonadic io
Feb 16, 2011

>>=
You can also get a peer from the Host::peers method I mention above, but as before you can't store it only use it.

Pie Colony
Dec 8, 2006
I AM SUCH A FUCKUP THAT I CAN'T EVEN POST IN AN E/N THREAD I STARTED
Doesn't Host::peers also require a mutable reference? I was fiddling with that but couldn't get it working.

e: if I call peers() after service() it maybe seems to work? Although I'm more confused now about why :)

Pie Colony fucked around with this message at 19:14 on Jan 9, 2021

Thom ZombieForm
Oct 29, 2010

I will eat you alive
I will eat you alive
I will eat you alive
https://foundation.rust-lang.org/posts/2021-02-08-hello-world/

Rust foundation formed

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

Not gonna lie, I got a bit verklempt when I saw that.

mdxi
Mar 13, 2006

to JERK OFF is to be close to GOD... only with SPURTING

Hello hello. I'm a ops/systems person. When I reach for a language to solve a problem with, my current languages of choice are bash, Python, and Go (roughly sorted in ascending order of problem complexity/solution durability).

I've been interested in Rust for years, but diving in and becoming fluent in a language is a non-trivial time commitment, and I don't feel under-resourced in terms of language support, so it has been rather down my priority list. A recent drive-by of the rust.org site to scope out changes since the last time I paid attention has made me think it might be time to make that commitment.

I see that Rust's async features have matured, have their own book now (though it isn't linked from the main docs page), and are discussed in the editions book as a major topic for the new edition. I've gotten really used to asking machines to do more than one thing at once, leaning pretty heavily on Go's baked-in concurrency (and management thereof), Python's concurrent futures module, and once having gone so far as to write my own worker pool implementation on top of Python 2.5's subprocess module (which didn't enjoy at all, but we do what we must when we're handed an out-of-date system and a ludicrous task).

So my questions, for people who have used Rust's async stuff are: how mature/settled would you say the support is? How would you say it compares to async tooling you've used in other languages? And if you didn't already know Rust, and async/concurrency was really important to you, would you dive in now, or would you wait for the next edition?

mdxi fucked around with this message at 21:43 on Feb 17, 2021

Progressive JPEG
Feb 19, 2003

async in Rust is sort of a PITA right now because they never standardized on an async runtime and now there are several incompatible ones with subtly different APIs.

So for example if you want to use hyper (http server/client library, good but reinvents a bunch of actor stuff internally), then either you use tokio (which I personally threw out in favor of smol, after tokio had months of API breakages in the run up to tokio 1.0) or put together a shim layer that will make hyper think it's running on tokio. Every async-using library ends up dealing with the same problems, and some have different wrappers to support multiple async runtimes, but most don't.

To summarize: Yes the base language async keywords are finally figured out, but I wouldn't call it mature at all given how the library ecosystem is now partitioned among mutually incompatible runtimes. At the same time, async support has IMO been way overhyped. For most use cases, direct threads/mutexes usually end up being faster anyway - and the ownership semantics quickly get complicated when defining the async tasks themselves. As such you may be better off just ignoring async while starting with Rust and maybe start looking at it later when you decide you're lacking that extra frustration in your life.

street doc
Feb 20, 2019

I just want to use actix and Postgres for a simple ‘hello world’ example. But it feels like a rabbit hole of complexity. Arctic using tokio? And futures? What asynchronous crate should I be using, in addition to actix?

gonadic io
Feb 16, 2011

>>=

street doc posted:

I just want to use actix and Postgres for a simple ‘hello world’ example. But it feels like a rabbit hole of complexity. Arctic using tokio? And futures? What asynchronous crate should I be using, in addition to actix?

To quickly answer your question: actix requires tokio, which is built on top of futures. You absolutely need all 3 depending on how low level you go.

To longer answer your question: why specifically have you decided to use actix? Do you mean actix-web? Especially for smaller stuff you can get by with just tokio and futures and don't need a big framework which would cut down the size of the rabbit hole.

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

street doc posted:

I just want to use actix and Postgres for a simple ‘hello world’ example. But it feels like a rabbit hole of complexity. Arctic using tokio? And futures? What asynchronous crate should I be using, in addition to actix?

i made a simple website using actix web and postgres last year. it wasn’t that complicated imho

xtal
Jan 9, 2011

by Fluffdaddy
I made one last weekend and it wasn't that hard. It is different from other languages where you have one framework to rule them all. You kind of need to piece together the web server, DB access, etc all separately. But it all is designed to work together and has good defaults. Honestly you can just copy the example actix-web skeleton with the stack you want and go from there.

street doc
Feb 20, 2019

fart simpson posted:

i made a simple website using actix web and postgres last year. it wasn’t that complicated imho

What dependency did you use for Postgres? Diesel?

Khorne
May 1, 2002

street doc posted:

What dependency did you use for Postgres? Diesel?
I'm a huge sqlx fan. It's straightforward and highly productive. It doesn't require learning yet another ORM and relying on all kinds of opaque abstractions. You define a struct (or don't), you write some SQL, you make a single call to get a resultset in the format you want. It's like using a scripting language's sql library but in Rust.

It also offers compile-time checking against the actual database to ensure correctness and handles types well. These are both opt-in rather than required, but they're certainly nice to have depending on the requirements of the project.

Khorne fucked around with this message at 18:28 on Mar 12, 2021

xtal
Jan 9, 2011

by Fluffdaddy
Sqlx is so much better than diesel its obscene.

gonadic io
Feb 16, 2011

>>=
I have had success with rusqlite directly (even if it's somewhat bare) but now I'm thinking about switching to the sqlx wrapper.

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

street doc posted:

What dependency did you use for Postgres? Diesel?

i used something called r2d2_postgres. dont remember much about it tbh

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

looking at it again and it looks like mostly what i did was stored procs in postgres and wrote a macro in rust to help build queries with the postgres tokio wrapper. r2d2 is just a connection pool thing

code:
macro_rules! build_query {
    (Vec<$type:ty>, $db:ident, $sql:literal, $args:expr, $res:tt) => {
        web::block(move || {
            let x: DBResult<Vec<$type>> = $db.get()
                .map_err(|e| DBError::PoolError(e))
                .and_then(|c| get(c, $sql, $args))
                .and_then($res);
            x
        })
        .await
    };

    ($type:ty, $db:ident, $sql:literal, $args:expr, $res:tt) => {
        web::block(move || {
            let x: DBResult<$type> = $db.get()
                .map_err(|e| DBError::PoolError(e))
                .and_then(|c| get_row(c, $sql, $args))
                .and_then($res);
            x
        })
        .await
    };
}

fn get(mut c: DBPool, query: &str, params: &[&(dyn tokio_postgres::types::ToSql + Sync)]) -> DBResult<Vec<tokio_postgres::row::Row>> {
    c.query(query, params).map_err(|e| DBError::TokioPostgresError(e))
}

fn get_row(mut c: DBPool, query: &str, params: &[&(dyn tokio_postgres::types::ToSql + Sync)]) -> DBResult<tokio_postgres::row::Row> {
    c.query_one(query, params).map_err(|e| DBError::TokioPostgresError(e))
}

fn get_from_row(row: tokio_postgres::row::Row) -> DBResult<String> {
    row.try_get(0).map_err(|e| DBError::TokioPostgresError(e))
}

pub async fn select_hello(db: web::Data<DB>) -> WebResult<String> {
    build_query!(
        String,
        db,
        "SELECT 'hello';",
        &[],
        { |row| get_from_row(row) }
    )
}
im sure theres a better way but that seemed to work for me???

fart simpson fucked around with this message at 16:29 on Mar 13, 2021

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
You could make the error handling a lot more concise using the ? operator and maybe a From impl or two, and that macro probably doesn't need to be a macro at all, and if you're using tokio postgres stuff you can probably just await it directly.

street doc
Feb 20, 2019

Both loadenv and dotenv are pulling up my system environment variables, but not the .env in the drat folder. Wtf

Workaday Wizard
Oct 23, 2009

by Pragmatica

street doc posted:

Both loadenv and dotenv are pulling up my system environment variables, but not the .env in the drat folder. Wtf

It might be a permissions issue. Check if the user running your application has access to .env file and that the file is in the current working directory.

This could happen if you accidentally create the .env file while root.

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

Ralith posted:

You could make the error handling a lot more concise using the ? operator and maybe a From impl or two, and that macro probably doesn't need to be a macro at all, and if you're using tokio postgres stuff you can probably just await it directly.

yeah probably. although if i ever decide to touch this again ill probably switch to that sqlx thing people were just talking about.

i remember there being a reason i made it a macro, like i tried it another way and it didn’t work. don’t remember why now

Hed
Mar 31, 2004

Fun Shoe
Dumb question about my Jetbrains IDE but this is really bumming me out:


Shouldn't the IDE know that glob is going to return enum `std::result::Result<Paths, PatternError>` instead of unknown?

If I change the line to:



so make an obviously wrong type, the linter complains with the right type, so I feel like this should be possible. I have all the relevant boxes checked in the PyCharm settings, is this due to the Jetbrains plugin or Windows jank?

Malloc Voidstar
May 7, 2007

Fuck the cowboys. Unf. Fuck em hard.
Worksforme on Windows with IDEA 2021.1. Which Pycharm are you on?

I'm using the nightly version of the plugin though, and I have it set to Maximum Features. As in new macro engine, new ("experimental") name resolution engine, build script handling enabled, stdlib metadata retrieval enabled, proc macro expansion enabled. Those last three can only be enabled via the Experimental Features windows (Help > Find Action).

Make sure you have the new macro engine and new name resolution engine enabled. If that doesn't do it maybe try the actually experimental features (but those do in fact break sometimes). (also none of those three should affect this case)

oh yeah and try File > Invalidate Caches > Invalidate and Restart as a last thing. It's been like a year I think but the plugin used to fail to invalidate outdated caches on occasion for me, leading to malfunctions

editedit: also if none of this helps, if you can post the full code (or a minimized variant) with the issue I'll try and figure out what's wrong

Malloc Voidstar fucked around with this message at 00:34 on Apr 23, 2021

Hed
Mar 31, 2004

Fun Shoe

Malloc Voidstar posted:

Worksforme on Windows with IDEA 2021.1. Which Pycharm are you on?

I'm using the nightly version of the plugin though, and I have it set to Maximum Features. As in new macro engine, new ("experimental") name resolution engine, build script handling enabled, stdlib metadata retrieval enabled, proc macro expansion enabled. Those last three can only be enabled via the Experimental Features windows (Help > Find Action).

Make sure you have the new macro engine and new name resolution engine enabled. If that doesn't do it maybe try the actually experimental features (but those do in fact break sometimes). (also none of those three should affect this case)

oh yeah and try File > Invalidate Caches > Invalidate and Restart as a last thing. It's been like a year I think but the plugin used to fail to invalidate outdated caches on occasion for me, leading to malfunctions

editedit: also if none of this helps, if you can post the full code (or a minimized variant) with the issue I'll try and figure out what's wrong

Sorry it took so long to reply. I decided to do some more research since I'm new to Rust and converting a program from Python. With a fresh set of eyes, I sat down and realized I hadn't namespaced the function. I popped in glob::glob() instead of glob() and everything immediately clicked!



Although, then it said the `use glob::glob` line was an unused import so I need to go read more about that stuff as well, since I would have thought `use glob::glob` would make it so that the glob() function I was using came from the glob crate. Thanks for your help, I'll need to remember the invalidate and restart cache thing in the future.

Workaday Wizard
Oct 23, 2009

by Pragmatica

Hed posted:

Sorry it took so long to reply. I decided to do some more research since I'm new to Rust and converting a program from Python. With a fresh set of eyes, I sat down and realized I hadn't namespaced the function. I popped in glob::glob() instead of glob() and everything immediately clicked!



Although, then it said the `use glob::glob` line was an unused import so I need to go read more about that stuff as well, since I would have thought `use glob::glob` would make it so that the glob() function I was using came from the glob crate. Thanks for your help, I'll need to remember the invalidate and restart cache thing in the future.

Just FYI you don't need `extern crate blah` for most cases anymore. You can just `use` whatever you want from the external crates directly.
More info: https://doc.rust-lang.org/nightly/edition-guide/rust-2018/module-system/path-clarity.html

panic state
Jun 11, 2019





This feels wrong.

Sagacity
May 2, 2003
Hopefully my epitaph will be funnier than my custom title.
You either have a gas leak or there are some snakes on the loose in your apartment.

panic state
Jun 11, 2019





smh borrow checker cramping my style

gonadic io
Feb 16, 2011

>>=

Ott_ posted:



smh borrow checker cramping my style

should be pretty quick to change that to
code:
let prev = buffers[i][s-1];
buffers[i][s] -= prev;
I think they talked about doing this kind of transformation automatically, since a similar thing happens with e.g. function args that sometimes you need to explicitly construct before calling the function

panic state
Jun 11, 2019



gonadic io posted:

should be pretty quick to change that to
code:
let prev = buffers[i][s-1];
buffers[i][s] -= prev;
I think they talked about doing this kind of transformation automatically, since a similar thing happens with e.g. function args that sometimes you need to explicitly construct before calling the function

Yeah that's what I did but it turns out that operation wasn't even what I was supposed to be doing. So I guess the compiler saved me in the end? :shrug:

astral
Apr 26, 2004

Just a friendly heads-up that Rust is one of the languages now officially supported in the recently-updated [code=thing] tags. Enjoy! I tweaked the language of a code block on this page to help show it off.

Full list of supported languages/language aliases available here.

gonadic io
Feb 16, 2011

>>=
sweet

Workaday Wizard
Oct 23, 2009

by Pragmatica
Testing...

Rust code:
// `elided_input` and `annotated_input` essentially have identical signatures
// because the lifetime of `elided_input` is inferred by the compiler:
fn elided_input(x: &i32) {
    println!("`elided_input`: {}", x);
}

fn annotated_input<'a>(x: &'a i32) {
    println!("`annotated_input`: {}", x);
}

// Similarly, `elided_pass` and `annotated_pass` have identical signatures
// because the lifetime is added implicitly to `elided_pass`:
fn elided_pass(x: &i32) -> &i32 { x }

fn annotated_pass<'a>(x: &'a i32) -> &'a i32 { x }

fn main() {
    let x = 3;

    elided_input(&x);
    annotated_input(&x);

    println!("`elided_pass`: {}", elided_pass(&x));
    println!("`annotated_pass`: {}", annotated_pass(&x));
}

Workaday Wizard
Oct 23, 2009

by Pragmatica
Neat! Good stuff admins.

gonadic io
Feb 16, 2011

>>=
Rust's GCC backend is getting merged, so now it (in theory) supports all targets that GCC supports:
https://github.com/rust-lang/compiler-team/issues/442#issuecomment-876358112

This was the main anti-rust argument I still kept hearing so it's going to be very interesting to see what people do with this on more niche and older platforms.

Adbot
ADBOT LOVES YOU

xgalaxy
Jan 27, 2004
i write code

gonadic io posted:

Rust's GCC backend is getting merged, so now it (in theory) supports all targets that GCC supports:
https://github.com/rust-lang/compiler-team/issues/442#issuecomment-876358112

This was the main anti-rust argument I still kept hearing so it's going to be very interesting to see what people do with this on more niche and older platforms.

I wonder if GCC will be better about not barfing on noalias pointers.. or does it even have that optimization that llvm has that keeps breaking?

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