Refactoring std for ultimate portability

grep for panic! is the simple answer.

Using a custom macro like pal_default_panic! would make the situation even more obvious. panic! might be used in other parts of the code, so grepping would give more than the relevant results. But that's just a detail.

unimplemented! already exists, and shouldn’t be used in std for anything other than the PAL defaults.

2 Likes

Extrapolating from those two blanket impls, there's the general principle of defining functions with a minimal error, and using where std::io:Error: From<MyMinimalError> in the std wrapper.

I expect Error can be handled with the inner-type pattern, where pal_common defines a common Error type with less features than the one in std.

Maybe that's still needed, but works just fine in conjunction to mine. Impls like Cursor<T>, &mut [u8], Buf* and the other wrappers ought to be perfectly portable and live below the PAL. I think that just lives the standard streams of things in std::io to be defined in the PAL.

I feel I have to mention that PAL is a great name. It’s the German name for the SEP from the Hitchhiker’s Guide to the Galaxy, and indeed for most Rust programmers, the PAL will be somebody else’s problem. :wink:

1 Like

I would love to see a future where everyone uses rust, or at last one where it is much easier to port rust to new platforms :wink:.

Wrt.:

and

some bikshedding/idea

I think both can be done with some think like singular (module?) interfaces , basically interfaces, which have exact one implementation chosen at compiler time at any time (ignoring the "multiple versions of the same crate" case). They can't be used wrt. generics but are only meant to say (at module level?) that some think of a certain structure exists (applicable to structs, enums, modules etc. inclusive possible partial variations of them). A crate could require such a interface to be satisfied (through other crates, possible depending on this crate) or could provide a implementation satisfying.

EDIT: just to clarify singular interfaces are also not meant to be used as trait objects (they are no traits) and don't have to specify the complete public interface the implementation satisfying them has, partial is ok as long as it doesn't conflict.

And while I think such interfaces would be nice for some cases of dependency inversion in combination with platform specify code I'm not sure if I would want see usage of it outside of this cases. And both the UI case and the test harness case mentioned by @DanielKeep can be solved differently, I think.

Related: Pre-RFC: providable

Thanks @jethrogb. The main difference to my idea is, that my idea is not bound to traits and doesn't use impl. With this you can use it for parts, requiring structs. E.g. defining that there has to be a struct PathBuf which's (non trait) impl has a method fn new() -> PathBuf etc. Also that std::path::PathBuf should be a valid path to access it.

is there a way, to make symbols public wrt. to building std. But private wrt. to using it. It probably don't work when rust parts of std are dynamically liked in, but if a single, possible dynamic linked in, std artifact is produced it could work, or? (I'm not saying we should do it, I just wonder if we could do it if it is needed)

When building a shared object you can choose which symbols to expose. So yes, if you were to build the entire runtime into a single shared object a lot of internal symbols would disappear. However, you'd of course lose all the pluggability you'd gain from using separate objects.

Could this functionality be provided by an annotation, using the #[] syntax? Maybe like #[provides(item.path)] for the implementation and #[providable] for the definition.

Maybe I misunderstand those annotations (I don’t know what they’re called in Rust).

I think so, you could see #[provides(item.path)] as some how analog to #[lang="some_lang_item"] but more general. An use #[providable] on a struct, struct+impl or trait. (through there might be some corner cases)

I will move this part of the discussion to the mentioned pre-rfc :smile:

Also while I think that this kind of mechanic can be a grate help for std and other bigger libraries where the logical seperation of components is not allways on the same plane as the os-specific/unspecific seperation, I don’t know if it would be good for the general rust language.

I think per function providables are a good idea.

@brson I’d like to keep moving on this. What would be a path forward on this? Reading this thread there weren’t really any objections to your proposal. Does someone just need to do the work to implement your proposal? Do we need to do some more design first? Do we need an RFC?

Let’s do this so we can more easily add CloudABI to Rust :slight_smile:

1 Like

@brson another ping now you’re back from your conference

@jethrogb Hi, I’m so glad you are interested, and I’m sorry for not following up sooner. I don’t think there are any obstacles to proceeding, as refactoring in this direction seems fairly unobjectionable.

There are a few ways to proceed, but I think getting to the point where you can implement a custom std out of tree will be a long road.

For refactoring std, the easiest thing to get started is by cutting down entries in the tidy whitelist, where std is not using conditions in the right modules. These are easy patches to make and review.

Someone could also be bold and create the pal_common / pal_unix / pal_windows crates and wire them up to the build system. I suspect that would get past review with some debate. After that there will be a long slog of puzzling out how to pull pieces out. I mentioned some other next steps in my op. And remember my linked branch has uncommitted refactoring steps that, while bitrotted, demonstrate some sequences that work to untangle dependencies.

As far as your end goal of creating a std port that excludes large chunks of std, for now I would focus on creating a port that fits into the model described here, where the customization is at the sys/pal layer. Today, I would suggest that, instead of trying to compile out parts of std your port doesn’t use, settle for panicking instead. In the longer term, when std has scenarios, then we can actually remove the unsupported features. This might involve a custom platform under the libstd/sys directory, and ultimately a custom pal crate.

1 Like

@jethrogb Any progress? I’d love to help.

Nothing yet and nothing planned for the next month and a half at least, so feel free to start anywhere you want.

2 Likes

I’ve been working on something the last few days. I started with std::fs, which was rather trivial to create an abstract layer for (since it’s already pretty cleanly seperated) and I’m currently shuffling around std::net. Would love to hear some thoughts about it.

edit: Whoops, forgot the link: https://github.com/rust-lang/rust/compare/master...panicbit:portability

Neat! I looked over the diff so far and it all looks reasonable to me. The main question I have that you might have a better look at since you’ve touched the code: does this actually reduce the interdependencies such that it could be separated out into a different crate?