Threads for marioom

  1. 2

    Hi all! Sorry for taking so long to release this article :P. Lots of stuff going on right now, and implementing the first version of the PDK also took me a lot of time.

    As always, it’s part of the Plugins in Rust series, but it includes other useful advice, in this case about refactoring. This will (very hopefully) be the second-to-last article, and after I’m done I’ll clean up the entire series and make them more clear for future readers.

    Any doubts/suggestions/etc please let me know!

    1. 2

      [I’m not a mod] Just a quick warning that “self promotion” in the sense of posting a lot of your own articles, without much interaction outside of these - or in quick succession, may result in a ban here. It’s usually better to let somebody else post them (on their own!), even more when it’s a series on the same topic. This also means that somebody apart from you (the author) found it worthwhile to post.

      I think it’s understandable if you don’t have much to comment on other stories, or when you have a series of posts on the same topic, but it’s also hard to draw a line here to people self-promoting their (company..) blog on lobsters and not interacting in any other way.

      1. 4

        I admittedly use Reddit more often than Lobsters; I was planning on starting to use Lobsters more frequently but it’s so many platforms at this point that I get lost. I initially created this account just because I thought it’d be better if I posted my own stuff instead of someone else, so that I can answer questions and etc. But you’re right that someone else posting it has more merit. FWIW, this blog is not tied to any company and it’s fully free for anyone.

        To be clear – I completely agree with you, just wanted to share my original reasons. I’ll try to either be more active or let someone else post my stuff. I fully respect the rules in here, it seems much better moderated than Reddit or Twitter, so props for that to the devs. Thanks for the tip, extremely helpful!

    1. 3

      Both this and the previous article on the series didn’t convince me you need Generics/Trait objects/etc in your plugin system. Do you have an example usage that’d show why this is necessary?

      1. 1

        How else would you maintain some kind of state in each plugin? I’ve considered two ways of doing it: (1) having a generic state type that’s passed to each call to the plugin, or (2) having the library export only a type that implements the functionality you need via a trait object, so that you can keep your state in the type itself instead. In the end both are the actually the same. The latter is easier to use because you can just have a dyn Plugin and use it like that from that moment on.

        1. 2

          What kind of state? Whether it’s loaded, active, valid, what’s its user config as text, what messages we have exchanged so far, where is it located in the filesystem if it’s a loadable code file, all that stuff doesn’t require generic types.

          If I have for example, a logging plugin (for the sake of an example) the state of whether I’m currently at WARN log level is something “private”. I wouldn’t have to export a type struct

          struct SomeGeneralLoggingPluginState {
          current_state: log::LogLevel,
          }
          

          in the plugin manager just to register Plugin<SomeGeneralLoggingPluginState>, would I?

          1. 1

            The state depends on the specific plugin you need. For example for the metronome connector plugin I needed to save the interval (configuration by the user), and the time where the next event should occur (internal).

            If I’ve understood your example correctly, you’d certainly need to declare that struct in one way or another in order to read/write the field with log::LogLevel. Even if it’s internal you still need access to it. It’d look like:

            struct SomeGeneralLoggingPluginState {
              current_state: log::LogLevel,
            }
            
            impl Plugin for SomeGeneralLoggingPluginState {
              fn run(&self) {
                event!(self.current_state, "Event")
              }
            }
            

            And then you’d export SomeGeneralLoggingPluginState as a dyn type, or whatever manual/abi_stable alternative you end up using. Plugin isn’t a struct, it’s a trait that defines the functionality required by the plugin. Then, you can use any combination of plugins under the same interface, which is our objective. There are certainly workarounds, such as using globals or a separate task with the data, but I found this to be the most natural and simple.

            Perhaps I’m misunderstanding your question?

            1. 3

              No that explains stuff. You want a plugin across FFI boundaries to implement some methods. I’d instead do a custom vtable:

              A plugin is:

              • A void opaque pointer, and
              • A struct of function pointers with the required methods, that probably take as first argument the plugin pointer

              You can register that with FFI easily, even from C. Using a trait for this is overcomplicating things. This pattern is used in large C codebases when you have “things” that can be “registered” and since you want FFI you can just use this pattern as well.

              1. 5

                It’s a shame that COM never took off in the *NIX ecosystem, because that’s exactly what COM provides: a language-agnostic vtable with some introspection features and an IDL for defining COM interfaces that you can use with language-specific code generators.

                1. 2

                  Exactly, using void pointers is one of the alternatives I mentioned and works just as well. It’s roughly what I used for the raw dynamic linking example, though somewhat differently because I didn’t explicitly create a vtable. I just preferred to use a trait because even though it may be slower, it’s way easier in Rust, and there’s no unsafe involved.

                  Perhaps the issue here is calling all of these alternatives “generics”. I know they aren’t generics, but they work for very similar usages. I did think about it when writing the articles but I thought “generics” would be more understandable than “polymorphism”.

                  1. 3

                    Well, whatever my opinion is worth, it’s that a trait/Rust-y solution is too complex and convoluted and maybe not really more useful in the future. Even if not, you can implement the easy opaque pointer and standard interface way now and convert into something Rust-ier in the future. All I read in your posts is that you’re putting more and more blocks together attempting to do something without a reason not to do it with the simple solution. (All this said in good faith, my honest opinion)

                    1. 2

                      No, I completely agree with you. I definitely get your point that abi_stable altogether may be too much complexity. I initially did everything possible to avoid it haha. But in my opinion, using traits with abi_stable is considerably easier to work with than opaque types and vtables. The only complicated part was to learn how to use abi_stable. Which I wanted to do anyway before jumping into a definitive solution to make sure I’m not missing anything.

                      Really, I just want to get the plugin system working first of all and then I’ll try to optimize in whichever ways I find best. I think that’ll be the most efficient way to get it working as I want to, because the whole plugin system idea is super complex and I don’t want to get lost in implementation details such as the solution you propose.

        1. 3

          Thanks for writing this, I’ve learned a lot personally!

          1. 3

            Oh my it’s a honor to teach matklad something! Love your blog as well <3