How to convert Vec<Rgb> to Vec
up vote
2
down vote
favorite
Using the Piston image
crate, I can write an image by feeding it a Vec<u8>
, but my actual data is Vec<Rgb<u8>>
(because that is a lot easier to deal with, and I want to grow it dynamically).
How can I convert Vec<Rgb<u8>>
to Vec<u8>
? Rgb<u8>
is really [u8; 3]
. Does this have to be an unsafe
conversion?
rust rust-piston
add a comment |
up vote
2
down vote
favorite
Using the Piston image
crate, I can write an image by feeding it a Vec<u8>
, but my actual data is Vec<Rgb<u8>>
(because that is a lot easier to deal with, and I want to grow it dynamically).
How can I convert Vec<Rgb<u8>>
to Vec<u8>
? Rgb<u8>
is really [u8; 3]
. Does this have to be an unsafe
conversion?
rust rust-piston
1
What does conversion mean in this case? What is the converted value ofvec![[10, 20, 30], [5, 15, 25]]
?
– trentcl
Nov 8 at 18:10
2
@trentcl I'm assuming flattened.
– Shepmaster
Nov 8 at 18:11
Well, to go the other way, I'd do it with.chunks(3)
, so I assume you could use.map(|x| x.iter()).flatten()
– Optimistic Peach
Nov 8 at 19:13
add a comment |
up vote
2
down vote
favorite
up vote
2
down vote
favorite
Using the Piston image
crate, I can write an image by feeding it a Vec<u8>
, but my actual data is Vec<Rgb<u8>>
(because that is a lot easier to deal with, and I want to grow it dynamically).
How can I convert Vec<Rgb<u8>>
to Vec<u8>
? Rgb<u8>
is really [u8; 3]
. Does this have to be an unsafe
conversion?
rust rust-piston
Using the Piston image
crate, I can write an image by feeding it a Vec<u8>
, but my actual data is Vec<Rgb<u8>>
(because that is a lot easier to deal with, and I want to grow it dynamically).
How can I convert Vec<Rgb<u8>>
to Vec<u8>
? Rgb<u8>
is really [u8; 3]
. Does this have to be an unsafe
conversion?
rust rust-piston
rust rust-piston
asked Nov 8 at 17:20
Timmmm
35.4k28189245
35.4k28189245
1
What does conversion mean in this case? What is the converted value ofvec![[10, 20, 30], [5, 15, 25]]
?
– trentcl
Nov 8 at 18:10
2
@trentcl I'm assuming flattened.
– Shepmaster
Nov 8 at 18:11
Well, to go the other way, I'd do it with.chunks(3)
, so I assume you could use.map(|x| x.iter()).flatten()
– Optimistic Peach
Nov 8 at 19:13
add a comment |
1
What does conversion mean in this case? What is the converted value ofvec![[10, 20, 30], [5, 15, 25]]
?
– trentcl
Nov 8 at 18:10
2
@trentcl I'm assuming flattened.
– Shepmaster
Nov 8 at 18:11
Well, to go the other way, I'd do it with.chunks(3)
, so I assume you could use.map(|x| x.iter()).flatten()
– Optimistic Peach
Nov 8 at 19:13
1
1
What does conversion mean in this case? What is the converted value of
vec![[10, 20, 30], [5, 15, 25]]
?– trentcl
Nov 8 at 18:10
What does conversion mean in this case? What is the converted value of
vec![[10, 20, 30], [5, 15, 25]]
?– trentcl
Nov 8 at 18:10
2
2
@trentcl I'm assuming flattened.
– Shepmaster
Nov 8 at 18:11
@trentcl I'm assuming flattened.
– Shepmaster
Nov 8 at 18:11
Well, to go the other way, I'd do it with
.chunks(3)
, so I assume you could use .map(|x| x.iter()).flatten()
– Optimistic Peach
Nov 8 at 19:13
Well, to go the other way, I'd do it with
.chunks(3)
, so I assume you could use .map(|x| x.iter()).flatten()
– Optimistic Peach
Nov 8 at 19:13
add a comment |
2 Answers
2
active
oldest
votes
up vote
6
down vote
accepted
The answer depends on whether you are fine with copying the data. If copying is not an issue for you, you can do something like this:
let img: Vec<Rgb<u8>> = ...;
let buf: Vec<u8> = img.iter().flat_map(|rgb| rgb.data.iter()).cloned().collect();
If you want to perform the conversion without copying, though, we first need to make sure that your source and destination types actually have the same memory layout. Rust makes very few guarantees about the memory layout of structs. It currently does not even guarantee that a struct with a single member has the same memory layout as the member itself.
In this particular case, the Rust memory layout is not relevant though, since Rgb
is defined as
#[repr(C)]
pub struct Rgb<T: Primitive>
pub data: [T; 3],
The #[repr(C)]
attribute specifies that the memory layout of the struct should be the same as an equivalent C struct. The C memory layout is not fully specified in the C standard, but according to the unsafe code guidelines, there are some rules that hold for "most" platforms:
- Field order is preserved.
- The first field begins at offset 0.
- Assuming the struct is not packed, each field's offset is aligned to the ABI-mandated alignment for that field's type, possibly creating unused padding bits.
- The total size of the struct is rounded up to its overall alignment.
As pointed out in the comments, the C standard theoretically allows additional padding at the end of the struct. However, the Piston image library itself makes the assumption that a slice of channel data has the same memory layout as the Rgb
struct, so if you are on a platform where this assumption does not hold, all bets are off anyway (and I couldnt' find any evidence that such a platform exists).
Rust does guarantee that arrays, slices and vectors are densely packed, and that structs and arrays have an alignment equal to the maximum alignment of their elements. Together with the assumption that the layout of Rgb
is as specified by the rules I quotes above, this guarantees that Rgb<u8>
is indeed laid out as three consecutive bytes in memory, and that Vec<Rgb<u8>>
is indeed a consecutive, densely packed buffer of RGB values, so our conversion is safe. We still need to use unsafe code to write it:
let p = img.as_mut_ptr();
let len = img.len() * mem::size_of::<Rgb<u8>>();
let cap = img.capacity() * mem::size_of::<Rgb<u8>>();
mem::forget(img);
let buf: Vec<u8> = unsafe Vec::from_raw_parts(p as *mut u8, len, cap) ;
If you want to protect against the case that there is additional padding at the end of Rgb
, you can check whether size_of::<Rgb<u8>>()
is indeed 3. If it is, you can use the unsafe non-copying version, otherwise you have to use the first version above.
3
The entire paragraph explaining why the programmer believes this to be safe should be a comment directly next to theunsafe
block.
– Shepmaster
Nov 8 at 20:13
1
C doesn't guarantee anything about potential padding bytes at the end of such structure. A valid implementation can aligndata
to 4 bytes for exemple. port70.net/~nsz/c/c11/n1570.html#6.7.2.1p14, port70.net/~nsz/c/c11/n1570.html#6.7.2.1p17, (C17 didn't change that). So#[repr(C)]
doesn't change the problem. That very rare and unexpected but that exist on some embedded system.
– Stargateur
Nov 8 at 23:37
@Stargateur When I wrote the answer, I looked up the rules ofrepr(C)
in the unsafe code guidelines – specifically "The total size of the struct is rounded up to its overall alignment." However, I missed that this is only valid for "most" platforms.
– Sven Marnach
Nov 9 at 9:10
@Stargateur While it's clear that the standard allows the additional padding, I'm not convinced that this can actually happen in practice. I tried to find evidence that there are targets that would actually add additional padding in this particular case, but couldn't find any. Do you have a link by any chance?
– Sven Marnach
Nov 9 at 9:31
Exemple (Of course, this use an extension for simplicity) but the idea is that the memory layout ofstruct data
is valid according to C ISO, Rust and C doesn't give a lot of guarantee about memory layout. C have few guarantee however but not the one you need to say "this is safe in any implementation". ABI C is implemented defined, there is NOT one standard ABI in C. ABI is not even quoted in the ISO. People can assume anything but that doesn't concern C.
– Stargateur
Nov 9 at 10:24
|
show 6 more comments
up vote
2
down vote
You choose the Vec<Rgb<u8>>
storage format because it's easier to deal with and you want it to grow dynamically. But as you noticed, there's no guarantee of compatibility of its storage with a Vec<u8>
, and no safe conversion.
Why not take the problem the other way and build a convenient facade for a Vec<u8>
?
type Rgb = [u8; 3];
#[derive(Debug)]
struct Img(Vec<u8>);
impl Img
fn new() -> Img
Img(Vec::new())
fn push(&mut self, rgb: &Rgb)
self.0.push(rgb[0]);
self.0.push(rgb[1]);
self.0.push(rgb[2]);
// other convenient methods
fn main()
let mut img = Img::new();
let rgb : Rgb = [1, 2, 3];
img.push(&rgb);
img.push(&rgb);
println!(":?", img);
For what it's worth, the image library basically provides such a façade in the form of theImageBuffer
struct, so the right answer is probably to simply use that.
– Sven Marnach
Nov 9 at 10:20
I didn't useImageBuffer
because it has a fixed width & height and as I noted in the question I want to grow byVec<>
dynamically.
– Timmmm
Nov 9 at 11:13
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
6
down vote
accepted
The answer depends on whether you are fine with copying the data. If copying is not an issue for you, you can do something like this:
let img: Vec<Rgb<u8>> = ...;
let buf: Vec<u8> = img.iter().flat_map(|rgb| rgb.data.iter()).cloned().collect();
If you want to perform the conversion without copying, though, we first need to make sure that your source and destination types actually have the same memory layout. Rust makes very few guarantees about the memory layout of structs. It currently does not even guarantee that a struct with a single member has the same memory layout as the member itself.
In this particular case, the Rust memory layout is not relevant though, since Rgb
is defined as
#[repr(C)]
pub struct Rgb<T: Primitive>
pub data: [T; 3],
The #[repr(C)]
attribute specifies that the memory layout of the struct should be the same as an equivalent C struct. The C memory layout is not fully specified in the C standard, but according to the unsafe code guidelines, there are some rules that hold for "most" platforms:
- Field order is preserved.
- The first field begins at offset 0.
- Assuming the struct is not packed, each field's offset is aligned to the ABI-mandated alignment for that field's type, possibly creating unused padding bits.
- The total size of the struct is rounded up to its overall alignment.
As pointed out in the comments, the C standard theoretically allows additional padding at the end of the struct. However, the Piston image library itself makes the assumption that a slice of channel data has the same memory layout as the Rgb
struct, so if you are on a platform where this assumption does not hold, all bets are off anyway (and I couldnt' find any evidence that such a platform exists).
Rust does guarantee that arrays, slices and vectors are densely packed, and that structs and arrays have an alignment equal to the maximum alignment of their elements. Together with the assumption that the layout of Rgb
is as specified by the rules I quotes above, this guarantees that Rgb<u8>
is indeed laid out as three consecutive bytes in memory, and that Vec<Rgb<u8>>
is indeed a consecutive, densely packed buffer of RGB values, so our conversion is safe. We still need to use unsafe code to write it:
let p = img.as_mut_ptr();
let len = img.len() * mem::size_of::<Rgb<u8>>();
let cap = img.capacity() * mem::size_of::<Rgb<u8>>();
mem::forget(img);
let buf: Vec<u8> = unsafe Vec::from_raw_parts(p as *mut u8, len, cap) ;
If you want to protect against the case that there is additional padding at the end of Rgb
, you can check whether size_of::<Rgb<u8>>()
is indeed 3. If it is, you can use the unsafe non-copying version, otherwise you have to use the first version above.
3
The entire paragraph explaining why the programmer believes this to be safe should be a comment directly next to theunsafe
block.
– Shepmaster
Nov 8 at 20:13
1
C doesn't guarantee anything about potential padding bytes at the end of such structure. A valid implementation can aligndata
to 4 bytes for exemple. port70.net/~nsz/c/c11/n1570.html#6.7.2.1p14, port70.net/~nsz/c/c11/n1570.html#6.7.2.1p17, (C17 didn't change that). So#[repr(C)]
doesn't change the problem. That very rare and unexpected but that exist on some embedded system.
– Stargateur
Nov 8 at 23:37
@Stargateur When I wrote the answer, I looked up the rules ofrepr(C)
in the unsafe code guidelines – specifically "The total size of the struct is rounded up to its overall alignment." However, I missed that this is only valid for "most" platforms.
– Sven Marnach
Nov 9 at 9:10
@Stargateur While it's clear that the standard allows the additional padding, I'm not convinced that this can actually happen in practice. I tried to find evidence that there are targets that would actually add additional padding in this particular case, but couldn't find any. Do you have a link by any chance?
– Sven Marnach
Nov 9 at 9:31
Exemple (Of course, this use an extension for simplicity) but the idea is that the memory layout ofstruct data
is valid according to C ISO, Rust and C doesn't give a lot of guarantee about memory layout. C have few guarantee however but not the one you need to say "this is safe in any implementation". ABI C is implemented defined, there is NOT one standard ABI in C. ABI is not even quoted in the ISO. People can assume anything but that doesn't concern C.
– Stargateur
Nov 9 at 10:24
|
show 6 more comments
up vote
6
down vote
accepted
The answer depends on whether you are fine with copying the data. If copying is not an issue for you, you can do something like this:
let img: Vec<Rgb<u8>> = ...;
let buf: Vec<u8> = img.iter().flat_map(|rgb| rgb.data.iter()).cloned().collect();
If you want to perform the conversion without copying, though, we first need to make sure that your source and destination types actually have the same memory layout. Rust makes very few guarantees about the memory layout of structs. It currently does not even guarantee that a struct with a single member has the same memory layout as the member itself.
In this particular case, the Rust memory layout is not relevant though, since Rgb
is defined as
#[repr(C)]
pub struct Rgb<T: Primitive>
pub data: [T; 3],
The #[repr(C)]
attribute specifies that the memory layout of the struct should be the same as an equivalent C struct. The C memory layout is not fully specified in the C standard, but according to the unsafe code guidelines, there are some rules that hold for "most" platforms:
- Field order is preserved.
- The first field begins at offset 0.
- Assuming the struct is not packed, each field's offset is aligned to the ABI-mandated alignment for that field's type, possibly creating unused padding bits.
- The total size of the struct is rounded up to its overall alignment.
As pointed out in the comments, the C standard theoretically allows additional padding at the end of the struct. However, the Piston image library itself makes the assumption that a slice of channel data has the same memory layout as the Rgb
struct, so if you are on a platform where this assumption does not hold, all bets are off anyway (and I couldnt' find any evidence that such a platform exists).
Rust does guarantee that arrays, slices and vectors are densely packed, and that structs and arrays have an alignment equal to the maximum alignment of their elements. Together with the assumption that the layout of Rgb
is as specified by the rules I quotes above, this guarantees that Rgb<u8>
is indeed laid out as three consecutive bytes in memory, and that Vec<Rgb<u8>>
is indeed a consecutive, densely packed buffer of RGB values, so our conversion is safe. We still need to use unsafe code to write it:
let p = img.as_mut_ptr();
let len = img.len() * mem::size_of::<Rgb<u8>>();
let cap = img.capacity() * mem::size_of::<Rgb<u8>>();
mem::forget(img);
let buf: Vec<u8> = unsafe Vec::from_raw_parts(p as *mut u8, len, cap) ;
If you want to protect against the case that there is additional padding at the end of Rgb
, you can check whether size_of::<Rgb<u8>>()
is indeed 3. If it is, you can use the unsafe non-copying version, otherwise you have to use the first version above.
3
The entire paragraph explaining why the programmer believes this to be safe should be a comment directly next to theunsafe
block.
– Shepmaster
Nov 8 at 20:13
1
C doesn't guarantee anything about potential padding bytes at the end of such structure. A valid implementation can aligndata
to 4 bytes for exemple. port70.net/~nsz/c/c11/n1570.html#6.7.2.1p14, port70.net/~nsz/c/c11/n1570.html#6.7.2.1p17, (C17 didn't change that). So#[repr(C)]
doesn't change the problem. That very rare and unexpected but that exist on some embedded system.
– Stargateur
Nov 8 at 23:37
@Stargateur When I wrote the answer, I looked up the rules ofrepr(C)
in the unsafe code guidelines – specifically "The total size of the struct is rounded up to its overall alignment." However, I missed that this is only valid for "most" platforms.
– Sven Marnach
Nov 9 at 9:10
@Stargateur While it's clear that the standard allows the additional padding, I'm not convinced that this can actually happen in practice. I tried to find evidence that there are targets that would actually add additional padding in this particular case, but couldn't find any. Do you have a link by any chance?
– Sven Marnach
Nov 9 at 9:31
Exemple (Of course, this use an extension for simplicity) but the idea is that the memory layout ofstruct data
is valid according to C ISO, Rust and C doesn't give a lot of guarantee about memory layout. C have few guarantee however but not the one you need to say "this is safe in any implementation". ABI C is implemented defined, there is NOT one standard ABI in C. ABI is not even quoted in the ISO. People can assume anything but that doesn't concern C.
– Stargateur
Nov 9 at 10:24
|
show 6 more comments
up vote
6
down vote
accepted
up vote
6
down vote
accepted
The answer depends on whether you are fine with copying the data. If copying is not an issue for you, you can do something like this:
let img: Vec<Rgb<u8>> = ...;
let buf: Vec<u8> = img.iter().flat_map(|rgb| rgb.data.iter()).cloned().collect();
If you want to perform the conversion without copying, though, we first need to make sure that your source and destination types actually have the same memory layout. Rust makes very few guarantees about the memory layout of structs. It currently does not even guarantee that a struct with a single member has the same memory layout as the member itself.
In this particular case, the Rust memory layout is not relevant though, since Rgb
is defined as
#[repr(C)]
pub struct Rgb<T: Primitive>
pub data: [T; 3],
The #[repr(C)]
attribute specifies that the memory layout of the struct should be the same as an equivalent C struct. The C memory layout is not fully specified in the C standard, but according to the unsafe code guidelines, there are some rules that hold for "most" platforms:
- Field order is preserved.
- The first field begins at offset 0.
- Assuming the struct is not packed, each field's offset is aligned to the ABI-mandated alignment for that field's type, possibly creating unused padding bits.
- The total size of the struct is rounded up to its overall alignment.
As pointed out in the comments, the C standard theoretically allows additional padding at the end of the struct. However, the Piston image library itself makes the assumption that a slice of channel data has the same memory layout as the Rgb
struct, so if you are on a platform where this assumption does not hold, all bets are off anyway (and I couldnt' find any evidence that such a platform exists).
Rust does guarantee that arrays, slices and vectors are densely packed, and that structs and arrays have an alignment equal to the maximum alignment of their elements. Together with the assumption that the layout of Rgb
is as specified by the rules I quotes above, this guarantees that Rgb<u8>
is indeed laid out as three consecutive bytes in memory, and that Vec<Rgb<u8>>
is indeed a consecutive, densely packed buffer of RGB values, so our conversion is safe. We still need to use unsafe code to write it:
let p = img.as_mut_ptr();
let len = img.len() * mem::size_of::<Rgb<u8>>();
let cap = img.capacity() * mem::size_of::<Rgb<u8>>();
mem::forget(img);
let buf: Vec<u8> = unsafe Vec::from_raw_parts(p as *mut u8, len, cap) ;
If you want to protect against the case that there is additional padding at the end of Rgb
, you can check whether size_of::<Rgb<u8>>()
is indeed 3. If it is, you can use the unsafe non-copying version, otherwise you have to use the first version above.
The answer depends on whether you are fine with copying the data. If copying is not an issue for you, you can do something like this:
let img: Vec<Rgb<u8>> = ...;
let buf: Vec<u8> = img.iter().flat_map(|rgb| rgb.data.iter()).cloned().collect();
If you want to perform the conversion without copying, though, we first need to make sure that your source and destination types actually have the same memory layout. Rust makes very few guarantees about the memory layout of structs. It currently does not even guarantee that a struct with a single member has the same memory layout as the member itself.
In this particular case, the Rust memory layout is not relevant though, since Rgb
is defined as
#[repr(C)]
pub struct Rgb<T: Primitive>
pub data: [T; 3],
The #[repr(C)]
attribute specifies that the memory layout of the struct should be the same as an equivalent C struct. The C memory layout is not fully specified in the C standard, but according to the unsafe code guidelines, there are some rules that hold for "most" platforms:
- Field order is preserved.
- The first field begins at offset 0.
- Assuming the struct is not packed, each field's offset is aligned to the ABI-mandated alignment for that field's type, possibly creating unused padding bits.
- The total size of the struct is rounded up to its overall alignment.
As pointed out in the comments, the C standard theoretically allows additional padding at the end of the struct. However, the Piston image library itself makes the assumption that a slice of channel data has the same memory layout as the Rgb
struct, so if you are on a platform where this assumption does not hold, all bets are off anyway (and I couldnt' find any evidence that such a platform exists).
Rust does guarantee that arrays, slices and vectors are densely packed, and that structs and arrays have an alignment equal to the maximum alignment of their elements. Together with the assumption that the layout of Rgb
is as specified by the rules I quotes above, this guarantees that Rgb<u8>
is indeed laid out as three consecutive bytes in memory, and that Vec<Rgb<u8>>
is indeed a consecutive, densely packed buffer of RGB values, so our conversion is safe. We still need to use unsafe code to write it:
let p = img.as_mut_ptr();
let len = img.len() * mem::size_of::<Rgb<u8>>();
let cap = img.capacity() * mem::size_of::<Rgb<u8>>();
mem::forget(img);
let buf: Vec<u8> = unsafe Vec::from_raw_parts(p as *mut u8, len, cap) ;
If you want to protect against the case that there is additional padding at the end of Rgb
, you can check whether size_of::<Rgb<u8>>()
is indeed 3. If it is, you can use the unsafe non-copying version, otherwise you have to use the first version above.
edited Nov 9 at 13:03
answered Nov 8 at 19:39
Sven Marnach
336k75737686
336k75737686
3
The entire paragraph explaining why the programmer believes this to be safe should be a comment directly next to theunsafe
block.
– Shepmaster
Nov 8 at 20:13
1
C doesn't guarantee anything about potential padding bytes at the end of such structure. A valid implementation can aligndata
to 4 bytes for exemple. port70.net/~nsz/c/c11/n1570.html#6.7.2.1p14, port70.net/~nsz/c/c11/n1570.html#6.7.2.1p17, (C17 didn't change that). So#[repr(C)]
doesn't change the problem. That very rare and unexpected but that exist on some embedded system.
– Stargateur
Nov 8 at 23:37
@Stargateur When I wrote the answer, I looked up the rules ofrepr(C)
in the unsafe code guidelines – specifically "The total size of the struct is rounded up to its overall alignment." However, I missed that this is only valid for "most" platforms.
– Sven Marnach
Nov 9 at 9:10
@Stargateur While it's clear that the standard allows the additional padding, I'm not convinced that this can actually happen in practice. I tried to find evidence that there are targets that would actually add additional padding in this particular case, but couldn't find any. Do you have a link by any chance?
– Sven Marnach
Nov 9 at 9:31
Exemple (Of course, this use an extension for simplicity) but the idea is that the memory layout ofstruct data
is valid according to C ISO, Rust and C doesn't give a lot of guarantee about memory layout. C have few guarantee however but not the one you need to say "this is safe in any implementation". ABI C is implemented defined, there is NOT one standard ABI in C. ABI is not even quoted in the ISO. People can assume anything but that doesn't concern C.
– Stargateur
Nov 9 at 10:24
|
show 6 more comments
3
The entire paragraph explaining why the programmer believes this to be safe should be a comment directly next to theunsafe
block.
– Shepmaster
Nov 8 at 20:13
1
C doesn't guarantee anything about potential padding bytes at the end of such structure. A valid implementation can aligndata
to 4 bytes for exemple. port70.net/~nsz/c/c11/n1570.html#6.7.2.1p14, port70.net/~nsz/c/c11/n1570.html#6.7.2.1p17, (C17 didn't change that). So#[repr(C)]
doesn't change the problem. That very rare and unexpected but that exist on some embedded system.
– Stargateur
Nov 8 at 23:37
@Stargateur When I wrote the answer, I looked up the rules ofrepr(C)
in the unsafe code guidelines – specifically "The total size of the struct is rounded up to its overall alignment." However, I missed that this is only valid for "most" platforms.
– Sven Marnach
Nov 9 at 9:10
@Stargateur While it's clear that the standard allows the additional padding, I'm not convinced that this can actually happen in practice. I tried to find evidence that there are targets that would actually add additional padding in this particular case, but couldn't find any. Do you have a link by any chance?
– Sven Marnach
Nov 9 at 9:31
Exemple (Of course, this use an extension for simplicity) but the idea is that the memory layout ofstruct data
is valid according to C ISO, Rust and C doesn't give a lot of guarantee about memory layout. C have few guarantee however but not the one you need to say "this is safe in any implementation". ABI C is implemented defined, there is NOT one standard ABI in C. ABI is not even quoted in the ISO. People can assume anything but that doesn't concern C.
– Stargateur
Nov 9 at 10:24
3
3
The entire paragraph explaining why the programmer believes this to be safe should be a comment directly next to the
unsafe
block.– Shepmaster
Nov 8 at 20:13
The entire paragraph explaining why the programmer believes this to be safe should be a comment directly next to the
unsafe
block.– Shepmaster
Nov 8 at 20:13
1
1
C doesn't guarantee anything about potential padding bytes at the end of such structure. A valid implementation can align
data
to 4 bytes for exemple. port70.net/~nsz/c/c11/n1570.html#6.7.2.1p14, port70.net/~nsz/c/c11/n1570.html#6.7.2.1p17, (C17 didn't change that). So #[repr(C)]
doesn't change the problem. That very rare and unexpected but that exist on some embedded system.– Stargateur
Nov 8 at 23:37
C doesn't guarantee anything about potential padding bytes at the end of such structure. A valid implementation can align
data
to 4 bytes for exemple. port70.net/~nsz/c/c11/n1570.html#6.7.2.1p14, port70.net/~nsz/c/c11/n1570.html#6.7.2.1p17, (C17 didn't change that). So #[repr(C)]
doesn't change the problem. That very rare and unexpected but that exist on some embedded system.– Stargateur
Nov 8 at 23:37
@Stargateur When I wrote the answer, I looked up the rules of
repr(C)
in the unsafe code guidelines – specifically "The total size of the struct is rounded up to its overall alignment." However, I missed that this is only valid for "most" platforms.– Sven Marnach
Nov 9 at 9:10
@Stargateur When I wrote the answer, I looked up the rules of
repr(C)
in the unsafe code guidelines – specifically "The total size of the struct is rounded up to its overall alignment." However, I missed that this is only valid for "most" platforms.– Sven Marnach
Nov 9 at 9:10
@Stargateur While it's clear that the standard allows the additional padding, I'm not convinced that this can actually happen in practice. I tried to find evidence that there are targets that would actually add additional padding in this particular case, but couldn't find any. Do you have a link by any chance?
– Sven Marnach
Nov 9 at 9:31
@Stargateur While it's clear that the standard allows the additional padding, I'm not convinced that this can actually happen in practice. I tried to find evidence that there are targets that would actually add additional padding in this particular case, but couldn't find any. Do you have a link by any chance?
– Sven Marnach
Nov 9 at 9:31
Exemple (Of course, this use an extension for simplicity) but the idea is that the memory layout of
struct data
is valid according to C ISO, Rust and C doesn't give a lot of guarantee about memory layout. C have few guarantee however but not the one you need to say "this is safe in any implementation". ABI C is implemented defined, there is NOT one standard ABI in C. ABI is not even quoted in the ISO. People can assume anything but that doesn't concern C.– Stargateur
Nov 9 at 10:24
Exemple (Of course, this use an extension for simplicity) but the idea is that the memory layout of
struct data
is valid according to C ISO, Rust and C doesn't give a lot of guarantee about memory layout. C have few guarantee however but not the one you need to say "this is safe in any implementation". ABI C is implemented defined, there is NOT one standard ABI in C. ABI is not even quoted in the ISO. People can assume anything but that doesn't concern C.– Stargateur
Nov 9 at 10:24
|
show 6 more comments
up vote
2
down vote
You choose the Vec<Rgb<u8>>
storage format because it's easier to deal with and you want it to grow dynamically. But as you noticed, there's no guarantee of compatibility of its storage with a Vec<u8>
, and no safe conversion.
Why not take the problem the other way and build a convenient facade for a Vec<u8>
?
type Rgb = [u8; 3];
#[derive(Debug)]
struct Img(Vec<u8>);
impl Img
fn new() -> Img
Img(Vec::new())
fn push(&mut self, rgb: &Rgb)
self.0.push(rgb[0]);
self.0.push(rgb[1]);
self.0.push(rgb[2]);
// other convenient methods
fn main()
let mut img = Img::new();
let rgb : Rgb = [1, 2, 3];
img.push(&rgb);
img.push(&rgb);
println!(":?", img);
For what it's worth, the image library basically provides such a façade in the form of theImageBuffer
struct, so the right answer is probably to simply use that.
– Sven Marnach
Nov 9 at 10:20
I didn't useImageBuffer
because it has a fixed width & height and as I noted in the question I want to grow byVec<>
dynamically.
– Timmmm
Nov 9 at 11:13
add a comment |
up vote
2
down vote
You choose the Vec<Rgb<u8>>
storage format because it's easier to deal with and you want it to grow dynamically. But as you noticed, there's no guarantee of compatibility of its storage with a Vec<u8>
, and no safe conversion.
Why not take the problem the other way and build a convenient facade for a Vec<u8>
?
type Rgb = [u8; 3];
#[derive(Debug)]
struct Img(Vec<u8>);
impl Img
fn new() -> Img
Img(Vec::new())
fn push(&mut self, rgb: &Rgb)
self.0.push(rgb[0]);
self.0.push(rgb[1]);
self.0.push(rgb[2]);
// other convenient methods
fn main()
let mut img = Img::new();
let rgb : Rgb = [1, 2, 3];
img.push(&rgb);
img.push(&rgb);
println!(":?", img);
For what it's worth, the image library basically provides such a façade in the form of theImageBuffer
struct, so the right answer is probably to simply use that.
– Sven Marnach
Nov 9 at 10:20
I didn't useImageBuffer
because it has a fixed width & height and as I noted in the question I want to grow byVec<>
dynamically.
– Timmmm
Nov 9 at 11:13
add a comment |
up vote
2
down vote
up vote
2
down vote
You choose the Vec<Rgb<u8>>
storage format because it's easier to deal with and you want it to grow dynamically. But as you noticed, there's no guarantee of compatibility of its storage with a Vec<u8>
, and no safe conversion.
Why not take the problem the other way and build a convenient facade for a Vec<u8>
?
type Rgb = [u8; 3];
#[derive(Debug)]
struct Img(Vec<u8>);
impl Img
fn new() -> Img
Img(Vec::new())
fn push(&mut self, rgb: &Rgb)
self.0.push(rgb[0]);
self.0.push(rgb[1]);
self.0.push(rgb[2]);
// other convenient methods
fn main()
let mut img = Img::new();
let rgb : Rgb = [1, 2, 3];
img.push(&rgb);
img.push(&rgb);
println!(":?", img);
You choose the Vec<Rgb<u8>>
storage format because it's easier to deal with and you want it to grow dynamically. But as you noticed, there's no guarantee of compatibility of its storage with a Vec<u8>
, and no safe conversion.
Why not take the problem the other way and build a convenient facade for a Vec<u8>
?
type Rgb = [u8; 3];
#[derive(Debug)]
struct Img(Vec<u8>);
impl Img
fn new() -> Img
Img(Vec::new())
fn push(&mut self, rgb: &Rgb)
self.0.push(rgb[0]);
self.0.push(rgb[1]);
self.0.push(rgb[2]);
// other convenient methods
fn main()
let mut img = Img::new();
let rgb : Rgb = [1, 2, 3];
img.push(&rgb);
img.push(&rgb);
println!(":?", img);
edited Nov 8 at 20:14
answered Nov 8 at 19:37
Denys Séguret
270k51568585
270k51568585
For what it's worth, the image library basically provides such a façade in the form of theImageBuffer
struct, so the right answer is probably to simply use that.
– Sven Marnach
Nov 9 at 10:20
I didn't useImageBuffer
because it has a fixed width & height and as I noted in the question I want to grow byVec<>
dynamically.
– Timmmm
Nov 9 at 11:13
add a comment |
For what it's worth, the image library basically provides such a façade in the form of theImageBuffer
struct, so the right answer is probably to simply use that.
– Sven Marnach
Nov 9 at 10:20
I didn't useImageBuffer
because it has a fixed width & height and as I noted in the question I want to grow byVec<>
dynamically.
– Timmmm
Nov 9 at 11:13
For what it's worth, the image library basically provides such a façade in the form of the
ImageBuffer
struct, so the right answer is probably to simply use that.– Sven Marnach
Nov 9 at 10:20
For what it's worth, the image library basically provides such a façade in the form of the
ImageBuffer
struct, so the right answer is probably to simply use that.– Sven Marnach
Nov 9 at 10:20
I didn't use
ImageBuffer
because it has a fixed width & height and as I noted in the question I want to grow by Vec<>
dynamically.– Timmmm
Nov 9 at 11:13
I didn't use
ImageBuffer
because it has a fixed width & height and as I noted in the question I want to grow by Vec<>
dynamically.– Timmmm
Nov 9 at 11:13
add a comment |
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53213025%2fhow-to-convert-vecrgbu8-to-vecu8%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
1
What does conversion mean in this case? What is the converted value of
vec![[10, 20, 30], [5, 15, 25]]
?– trentcl
Nov 8 at 18:10
2
@trentcl I'm assuming flattened.
– Shepmaster
Nov 8 at 18:11
Well, to go the other way, I'd do it with
.chunks(3)
, so I assume you could use.map(|x| x.iter()).flatten()
– Optimistic Peach
Nov 8 at 19:13