no_std support#528
Conversation
We use the [`alloc`](https://doc.rust-lang.org/alloc/) create, which requires the user define a global allocator. * Import from `core` and `alloc` instead of `std` * Create `std` feature * Create `use-bare-io` feature which adds a [bare-io](https://github.com/bbqsrc/bare-io) dependency to polyfill `std::io` features. This is an experimental feature and should be used with extreme caution. * Created an `io` module to polyfill `std::io` with `bare-io` in `no_std`. * Replace `HashMap` and `HashSet` with `BTreeMap` and `BTreeSet` just to get something working. This needs to be fixed. * CI runs tests `no_std` code
Cargo.toml
Outdated
| base64-compat = { version = "1.0.0", optional = true } | ||
| bech32 = "0.7.2" | ||
| bitcoin_hashes = "0.9.0" | ||
| bitcoin_hashes = { git = "https://github.com/justinmoon/bitcoin_hashes.git", branch = "more_no_std", features = [], default-features = false } |
There was a problem hiding this comment.
|
|
||
| impl ::std::error::Error for Bip34Error {} | ||
| #[cfg(feature = "std")] | ||
| impl std::error::Error for Bip34Error {} |
There was a problem hiding this comment.
I don't quite understand what a lack of an Error impl in `no_std would mean here ...
There was a problem hiding this comment.
Not too much. The whole Error concept is std-only usually. The Result type doesn't require the second generic parameter to be Error, so it has very few effects.
| pub use bare_io::*; | ||
|
|
||
| #[cfg(feature = "std")] | ||
| pub use std::io::*; |
There was a problem hiding this comment.
Any ideas on a better way to polyfill std::io?
There was a problem hiding this comment.
What things need to be polyfilled exactly?
I mean at some level we can just feature-gate all stuff that needs std things, but I suppose your concern is that the fraction that would remain is so small that a polyfill could make more things no-std-compatible?
How does a polyfill version of Vec work?
There was a problem hiding this comment.
F.e. if on-stack Vec's have a hard length requirement, that may cause various errors in different places as we assume that Vec's have infinite space. (F.e. when writing/encoding into them, we always unwrap errors so a length limit would cause these to panic.)
There was a problem hiding this comment.
The polyfill is currently only for std::io. We're using alloc::Vec, which is exactly the same as std::Vec.
|
I added another commit updating dependencies to get this running on real microcontrollers, as demonstrated in this repo. As for MSRV,
|
`std::collections::HashMap` and `std::collections::HashSet` don't have any analogue in `alloc` because they require a OS RNG. [Recently no_std-compatible implementations have been merged](rust-lang/rust#58623) but I think we'd need to massively increase our MSRV. So I opted to move any `HasMap` or `HasSet` usage in tests to `BTreeMap` or `BTreeSet`, and to gate the only non-test usage of `HashMap` or `HashSet` (in the merkle block code which won't be helpful on MCUs) behind a "std" feature flag. Public API doesn't change.
Upgraded bare-io to a branch which adds a feature to disable the `#[non_exhaustive]` attribute, which wasn't stabilized until 1.40.0.
|
Now this works on stable 1.36.0, which is where |
Kixunil
left a comment
There was a problem hiding this comment.
Making modifications to tests is not needed IMO, running tests without std doesn't make sense.
For cases where HashMap, HashSet are needed outside of tests a trait can be used to avoid hard dependency on std.
| #[test] | ||
| fn str_roundtrip() { | ||
| let mut unique = HashSet::new(); | ||
| let mut unique = BTreeSet::new(); |
There was a problem hiding this comment.
Should not be needed in tests, there's no point in making tests no_std
|
|
||
| use std::default::Default; | ||
| use core::default::Default; | ||
| use alloc::vec::Vec; |
There was a problem hiding this comment.
I believe it's possible to avoid bumping MSRV if use like this is conditional. Assuming it's fine to have diferent MSRV for different features (I think it's reasonable).
| //! ``` | ||
|
|
||
| #[cfg(feature="std")] | ||
| use std::collections::HashSet; |
There was a problem hiding this comment.
This is removed here but not anywhere below, how it's possible?
|
It looks like this needs a rather significant rebase? |
|
@TheBlueMatt Yea I'm going to tackle this over next week. |
|
Before rebasing this it probably would be best to look which dependencies need PRs merged and updates published first, otherwise I see you doing the same again in a month or two once all the dependencies are updated to have a no_std option. |
I think it makes sense to take a |
|
Any updates or help wanted? This would be very helpful for us. |
| //! software. | ||
| //! | ||
|
|
||
| #![cfg_attr(not(feature = "std"), no_std)] |
There was a problem hiding this comment.
as suggested also by @Kixunil there is no need to prevent use of std in tests
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
|
After #603 got merged I assume this one can be closed |
Use the
alloccreate, which requires the user define a global allocator.coreandallocinstead ofstdstdfeatureuse-bare-iofeature which adds abare-io dependency to polyfill
std::iofeatures. This is an experimental feature and should beused with extreme caution. Is using a 3rd party crate acceptable for this?
iomodule to actually do ^^HashMapandHashSetwithBTreeMapandBTreeSetjust toget something working. This needs to be fixed.
no_stdcodeCloses #409