Declaring array using a constant expression for its size - arrays

I have a newtype wrapper around an array. I assumed that I could use size_of instead of manually passing the size of the array around, but the compiler thinks I'm wrong.
use std::mem::{size_of, size_of_val};
#[repr(C, packed)]
struct BluetoothAddress([u8, ..6]);
fn main() {
const SIZE: uint = size_of::<BluetoothAddress>();
let bytes = [0u8, ..SIZE];
println!("{} bytes", size_of_val(&bytes));
}
(playpen link)
I'm using the nightly: rustc 0.13.0-nightly (7e43f419c 2014-11-15 13:22:24 +0000)
This code fails with the following error:
broken.rs:9:25: 9:29 error: expected constant integer for repeat count, found variable
broken.rs:9 let bytes = [0u8, ..SIZE];
^~~~
error: aborting due to previous error
The Rust Reference on Array Expressions makes me think that this should work:
In the [expr ',' ".." expr] form, the expression after the ".." must be a constant expression that can be evaluated at compile time, such as a literal or a static item.

Your SIZE definition is not legal; it’s just that the errors in it occur later than the error on the array construction. If you change [0u8, ..SIZE] to [0u8, ..6] just so that that part works, you find the problems with the SIZE declaration:
<anon>:7:24: 7:53 error: function calls in constants are limited to struct and enum constructors [E0015]
<anon>:7 const SIZE: uint = size_of::<BluetoothAddress>();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<anon>:7:24: 7:51 error: paths in constants may only refer to items without type parameters [E0013]
<anon>:7 const SIZE: uint = size_of::<BluetoothAddress>();
^~~~~~~~~~~~~~~~~~~~~~~~~~~
You simply can’t call size_of like that at present.
An alternative is to invert things so that SIZE is the canonical definition and the other places use it:
use std::mem::{size_of, size_of_val};
const SIZE: uint = 6;
#[repr(C, packed)]
struct BluetoothAddress([u8, ..SIZE]);
fn main() {
let bytes = [0u8, ..SIZE];
println!("{} bytes", size_of_val(&bytes));
}
Update: With Rust 1.0, this question has been effectively obsoleted, and the compiler error messages have been improved so that they are even more clear.
Furthermore, with #42859 recently landed, rustc nightly will allow using size_of in a constant context, provided the crate has #![feature(const_fn)] (and when #43017 lands, that won’t be needed any more either, and then it will filter through to stable).
In other words, improvements to the language have made this no longer an issue.

Related

How to use const pointers inside structures?

hope everyone is doing great!
I was trying to implement some lib in embbeded enviroment, where i need to use const pointers.
Here is the code i was trying to do:
#include <stdio.h>
int a = 10;
typedef struct {
void * const payload;
int size;
} msg;
typedef struct {
void * const payload2encode;
int size2encode;
} encode_msg;
msg message[1] = {
{
/* payload */ &a,
0
}
};
encode_msg encoded_message = {
/* payload */ message[0].payload,
0
};
int main(void){
return 0;
}
but i'm getting the following error:
main.c:23:19: error: initializer element is not constant 23 |
/* payload */ message[0].payload,
If I change 'message[0].payload' to '&a' the code runs fine. I don't get why this is happening :(. Anyone know why?
Best reggards!
There are a lot of misconceptions here.
First of all, why do you need a "constant pointer"? This message here:
error: initializer element is not constant
Does not mean "the compiler wants a constant pointer", but rather "I want a a compile-time constant such as an integer constant expression". This has nothing to do with const at all in C (C++ is another story), but this:
A variable with static storage duration, like one declared at file scope outside any function, must be initialized with a constant expression. Such as an integer constant expression or in case of pointers the address of another variable. But it cannot be initialized to the value of a different variable. Simple example:
int a = 0;
int b = a; // error: initializer element is not constant
That's just how the language works. You could initialize a pointer to an address however, since address constants are constant expressions. That's why /* payload */ &a, works.
Next misconception: there is nothing called "constant pointer". There are:
pointers to read-only data, const type* name.
read-only pointers to read/write data: type* const name.
read-only pointers to read-only data: const type* const name.
For some reason you picked the second of these options (took a gamble?). It has limited uses, but one valid use of it is to declare read-only pointer variables in ROM on embedded systems with true ROM like flash.
Also we usually don't want to use qualifiers like const inside a struct on an individual member if we can avoid it. Because then the struct variable itself ends up with different qualifiers than it's members. Normally we want the whole struct to be const or nothing in the struct at all. In embedded systems we also need to consider if we want it to be stored in RAM or flash.
Regarding void*, they are not very suitable to use for any purpose in an embedded system. uint8_t* boiling down to a character pointer is much more useful, since it can be used to access any other type byte by byte. Whereas a void* has to be converted to another type before we can do anything meaningful with it. Type-generic programming in embedded systems is also a bad idea most of the time, since such systems should behave deterministically.
encoded_message has static storage duration (because it's declared outside any function) and so must be initialized with a constant expression, and message[0].payload isn't (it's a const variable, but not a constant expression).
You can either declare encoded_message in a function (main, for instance), or, if your compiler supports it, make message const as well.
See also : Error "initializer element is not constant" when trying to initialize variable with const

Why I cant declare a constant in a function?

I am getting this error when I try to execute the following function
src\main.c(41): error: #259: constant value is not known
The code:
uint8_t checksum_tx(uint64_t Data, uint8_t dataLength, uint8_t checksum_len ) {
const uint8_t length = (dataLength + checksum_len - 1) / checksum_len;
//** splitting data into 6-bits subunits . . .
uint8_t res = 0U;
uint8_t dataSubUnit[length];
the line that causes the error
const uint8_t length = (dataLength + checksum_len - 1) / checksum_len;
could someone clarify what is going wrong?
I read somewhere the constants in C must be declared directly, it is not allowed to initialize them, I guess that is what I have done.
This is a limitation of the language... or a feature (depends on who reads it) for the const tagged object. I think your interpretation of the const keyword, applying to a data object with a limited life, should make it immutable for the life of the object, this is, the execution of the block in which it is defined. But the compiler makes the data effectively a constant (like the constant PI) and it must hold the same value for the whole program life, and not on each function invocation. In the case you post, the value of the expression that the constant will have is not known until the function is started and the parameters receive their values (only then can the initialization expression be evalutated), so the constant is not actually a constant (You could provide different values for the function parameters in different call and make the constant value different than in the previous call).
In java, for example, there's a final keyword to mark a data value as constant, and you must preserve (you are not allowed to assign/change values to it) during the life of the object (this meaning since it is created until it is destroyed), but the initial value is determined dynamically, at the time of creation. This is not true in C. A const declared data object must be actually a constant, and it must be initialized by an expression (called a const expression) whose value can be determined at compilation time.
Constants are not real variables: once the code is compiled constants are translated into hard-coded values (immediate operands), or references within a section of the program that cannot be written (usually .rodata).
So, constants can't be assigned a value at run-time. You have to set a value that is known at compilation time, hence a value that does not depend on other variable(s). (e.g. const int length = 42; ).
If you cannot know the value of length before compiling the code, then you need a variable, not a const.
I guess you needed a constant to declare the array dataSubUnit. The usual way to solve this is to declare an array large enough to contain any possible length:
#define MAX_DATA_SUBUNIT_LEN 42
uint8_t checksum_tx(uint64_t Data, ... ) {
uint8_t dataSubUnit[MAX_DATA_SUBUNIT_LEN];
... and to prevent a possible overflow:
uint8_t length = (dataLength + checksum_len - 1) / checksum_len;
if ( length < MAX_DATA_SUBUNIT_LEN ) {
... // proceed
}
else {
// return some relevant error code
}

What expressions are allowed as the array length N in [_; N]?

Please consider the following minimal example in Rust:
const FOOBAR: usize = 3;
trait Foo {
const BAR: usize;
}
struct Fubar();
impl Foo for Fubar {
const BAR: usize = 3;
}
struct Baz<T>(T);
trait Qux {
fn print_bar();
}
impl<T: Foo> Qux for Baz<T> {
fn print_bar() {
println!("bar: {}", T::BAR); // works
println!("{:?}", [T::BAR; 3]); // works
println!("{:?}", [1; FOOBAR]); // works
println!("{:?}", [1; T::BAR]); // this gives an error
}
}
fn main() {
Baz::<Fubar>::print_bar();
}
The compiler gives the following error:
error[E0599]: no associated item named `BAR` found for type `T` in the current scope
--> src/main.rs:24:30
|
24 | println!("{:?}", [1; T::BAR]); // this gives an error
| ^^^^^^ associated item not found in `T`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `BAR`, perhaps you need to implement it:
candidate #1: `Foo`
Whatever the answer to my question, this is not a particularly good error message because it suggests that T does implement Foo despite the latter being a trait bound. Only after burning a lot of time did it occur to me that in fact T::BAR is a perfectly valid expression in other contexts, just not as a length parameter to an array.
What are the rules that govern what kind of expressions can go there? Because arrays are Sized, I completely understand that the length are to be known at compile time. Coming from C++ myself, I would expect some restriction akin to constexpr but I have not come across that in the documentation where it just says
A fixed-size array, denoted [T; N], for the element type, T, and the non-negative compile-time constant size, N.
As of Rust 1.24.1, the array length basically needs to either be a numeric literal or a "regular" constant that is a usize. There's a small amount of constant evaluation that exists today, but it's more-or-less limited to basic math.
a perfectly valid expression in other contexts, just not as a length parameter to an array
Array lengths don't support generic parameters. (#43408)
this is not a particularly good error message
Error message should be improved for associated consts in array lengths (#44168)
I would expect some restriction akin to constexpr
This is essentially the restriction, the problem is that what is allowed to be used in a const is highly restricted at the moment. Notably, these aren't allowed:
functions (except to construct enums or structs)
loops
multiple statements / blocks
Work on good constant / compile-time evaluation is still ongoing. There are a large amount of RFCs, issues, and PRs improving this. A sample:
Const fn tracking issue (RFC 911)
Allow locals and destructuring in const fn (RFC 2341)
Allow if and match in constants (RFC 2342)

Why is it impossible to initialize a constant variable after its declaration?

This is not a problem, but I'd like to understand the following behaviour:
int main() {
// This does not work
int const a;
a = 50;
// This work
int const a = 50;
}
Why does the compiler throw the following error:
main.c:4:7: error: assignment of read-only variable ‘a’
I don't understand why even the initialization is forbidden. The line 3 has no translation in the assembly language. Does the compiler not detect that the line 4 is the first affectation (translation issue: I meant "assignment")?
EDIT: Ok, so let's say this is how the C language is. But this can be a problem because when I use C89, the declarations must be at the top of the functions and I can't use constant variables because assignments must be placed after the declarations . The only solution is to declare non-const variables or initialize all the variables. I find this "dirty".
I don't understand why even the initialization is forbidden.
The key is in the error message:
error: assignment of read-only variable ‘a’
It isn't an initialization, it is an assignment. An assignment modifies an existing object, and that is not permitted if said object is const.
This, on the other hand, despite using the = syntax, is an initialization:
int const a = 50;
Because it's a constant!
a = 50; is an assignment, not an initialisation.
int const a; essentially sets a to an indeterminant value (which you should never read by the way). Perhaps your compiler will warn you of this if you ask it nicely.
" Does the compiler not detect that the line 4 is the first affectation?"
It could, in this simple case. However the rules are written to cover all cases of initialization.
Compiler looks at one file at a time. A program may be composed of many files. The language allows variables to be declared in one translation unit (file) and used in another.
The rules are written so they take care of all such cases

Array definition - Expression must have a constant value

I am creating an array on stack as
static const int size = 10;
void foo() {
..
int array[size];
..
}
However, I get the compile error: "expression must have a constant value", even though size is a constant. I can use the macro
#define SIZE (10)
But I am wondering why size marked const causes compilation error.
In C language keyword const has nothing to do with constants. In C language, by definition the term "constant" refers to literal values and enum constants. This is what you have to use if you really need a constant: either use a literal value (define a macro to give your constant a name), or use a enum constant.
(Read here for more details: Shall I prefer constants over defines?)
Also, in C99 and later versions of the language it possible to use non-constant values as array sizes for local arrays. That means that your code should compile in modern C even though your size is not a constant. But you are apparently using an older compiler, so in your case
#define SIZE 10
is the right way to go.
The answer is in another stackoverflow question, HERE
it's because In C objects declared with the const modifier aren't true
constants. A better name for const would probably be readonly - what
it really means is that the compiler won't let you change it. And you
need true constants to initialize objects with static storage (I
suspect regs_to_read is global).
if you are on C99 your IDE compiler option may have a thing called variable-length array (VLA) enable it and you won't get compile error, efficiently without stressing your code though is with MALLOC or CALLOC.
static const int size = 10;
void foo() {
int* array;
array = (int *)malloc(size * sizeof(int));
}

Resources