Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

as_ne_bytes for primitive types #64464

Closed
hch12907 opened this issue Sep 14, 2019 · 8 comments · Fixed by #85679
Closed

as_ne_bytes for primitive types #64464

hch12907 opened this issue Sep 14, 2019 · 8 comments · Fixed by #85679
Labels
C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@hch12907
Copy link
Contributor

hch12907 commented Sep 14, 2019

Recently I've encountered a problem, when I wanted to return a slice of bytes in an integer:

fn to_bytes(num: &u32) -> &[u8] {
    &u32::to_le_bytes(*num)
}

(This is an oversimplification of the original code, which is generic over every integer type along with other stuff)

Obviously, since to_le_bytes(*num) is dropped when the function returns, this piece of code failed the borrow check. Hence my need for as_le_bytes(num), as num is actually alive all this time!

@jonas-schievink jonas-schievink added C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Sep 14, 2019
@Mark-Simulacrum
Copy link
Member

In this case I believe you can do this via a cast, e.g. unsafe { slice::from_raw_parts(num as *const u32 as *const u8, 4) } but I'm not sure if that's entirely well-defined, in particular, the u8 slice would then I believe be over-aligned.

It does seem viable that we could provide something like this, though.

@ExpHP
Copy link
Contributor

ExpHP commented Sep 16, 2019

The only method that can possibly be implemented is as_ne_bytes. (native endian)

as_le_bytes would be impossible to implement on BE systems, for precisely the same reasons that you cannot write your own function. Sure, num may be alive, but that would not matter because the memory associated with num would not contain those bytes in little-endian order. (and rust slices always have a stride of +1; you can't have a "backwards" slice)

but I'm not sure if that's entirely well-defined, in particular, the u8 slice would then I believe be over-aligned.

I daresay I've never heard anyone mention over-alignment before as a concern to even think about! (except in the context of deallocation, which doesn't matter here)

@hch12907
Copy link
Contributor Author

It is possible to workaround that by creating a struct, which is a reversed iterator, and return that struct instead for non-ne functions.

That might seem to be too much work for such a simple function though... Or we can just implement as_ne_bytes only.

@ExpHP
Copy link
Contributor

ExpHP commented Sep 22, 2019

I think the benefits of an iterator over the existing to_{b,l}e_bytes methods seem nebulous. That is to say, in whatever use case motivates this feature request (where the author needs a slice instead of an array), I can't imagine how an iterator would help.

@hch12907 hch12907 changed the title as_*e_bytes for primitive types as_ne_bytes for primitive types Sep 11, 2020
@hch12907
Copy link
Contributor Author

Implemented in #76610 for both floats and integers, under the feature gate int_as_bytes and float_as_bytes respectively.

bors added a commit to rust-lang-ci/rust that referenced this issue Oct 4, 2020
Implement as_ne_bytes() for integers and floats

This is related to issue rust-lang#64464.

I am pretty sure that these functions are actually const-ify-able, and technically as_bits() can also be implemented for floats, but I might need some comments on both.
@SimonSapin
Copy link
Contributor

Why is a borrow useful? Can’t the calling code assign the result of to_*e_bytes to a local variable and borrow that?

@hch12907
Copy link
Contributor Author

It gets problematic when generics are involved:

trait NumberAsBytes {
    fn as_bytes(&self) -> &[u8];
}

// fn as_bytes(&self) -> &[u8] { self.as_ne_bytes() }
impl_num_as_bytes!(i8, u8, /* ... */, i64, u64);

A ToBytes trait is also not doable (at least not in stable AFAIK), since different-sized integers give off a different [u8; N].

@SimonSapin
Copy link
Contributor

If a library wants to be general-purpose enough to expose such a trait, can’t it afford to do the unsafe conversion by itself? Does this really need to be in the standard library?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants