Is there a way to get C99 array designators or alternative in Rust?
Asked Answered
L

3

6

I've just completed a CodeWars kata on vowel-counting in C & Rust. The sample code is easy & obvious. Arrays can be used as fast mappings. Here, I map characters to logical values (0 or 1).

C implementation:

#include <stddef.h>
#include <stdint.h>

// :) This const array design looks smart & IMO-readable. Use of C99 feature.
const uint8_t areVowels[256]= {['a']=1, ['e']=1, ['i']=1, ['o']=1,  ['u']=1};

size_t get_count(const unsigned char *s)
{
  auto size_t count= 0;
  for (;*s!='\0';s++){
    count+= areVowels[*s];
  }
  return count;
}

Rust implementation:

// :( Here is me pain. Unreadable, python3-generated array content.
const ARE_VOWELS:[u8;256]= [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];

fn get_count(s: &str) -> usize {
  let mut vowels_count: usize = 0;

  for c in s.bytes(){
      vowels_count+= ARE_VOWELS[c as usize] as usize;
  }
  
  return vowels_count;
}

I wish I knew an alternative for array designators in Rust, which are a useful C99 feature. Initialization of the same byte array is much more awkward in my Rust code.

Littleton answered 22/5, 2022 at 17:42 Comment(1)
Ian answer is good. But this solution to the problem is not rusty, imo.Quinacrine
C
10

static mut is unnecessary here (and also bad practice). You can assign the value of a constant to the value of a code block which is capable of being evaluated in a const context

const ARE_VOWELS: [u8;256] = {
    let mut data = [0u8; 256];
    data['a' as usize] = 1;
    data['e' as usize] = 1;
    data['i' as usize] = 1;
    data['o' as usize] = 1;
    data['u' as usize] = 1;
    data
};

See this answer for prettying this up with a macro.

Chanukah answered 22/5, 2022 at 17:47 Comment(0)
D
7

An alternative is a macro that provides the syntax you want, its actually not too hard to make a general purpose implementation:

macro_rules! array {
    ($def:expr; $len:expr; $([$idx:expr]=$val:expr),* $(,)?) => { {
        let mut a = [$def; $len];
        $(a[$idx] = $val;)*
        a
    } }
}
let data = array![0; 256; ['a' as usize]=1, ['e' as usize]=1];

You can of course move the as usize into the macro if you like.

Daven answered 22/5, 2022 at 18:8 Comment(2)
Oh hey look, there's a crate that does this and more: array-litDaven
I really like that macro. It's easy & readable, and I've just rewritten it in my way.Littleton
L
0

I found some (unsafe ⇒ amusing) work around with "mut static value runtime initialization".

const VOWELS:&[u8;5]= b"aeiou";
static mut ARE_VOWELS:[u8;256]= [0;256];

fn init(){
    unsafe{
        let ptr: *mut u8= &mut ARE_VOWELS[0];
        for b in VOWELS{
            *(ptr.offset(*b as isize))= 1;
        }
    }
}

fn get_count(s: &str) -> usize {
    init();
    
    let mut vowels_count: usize = 0;

    for c in s.bytes(){
        unsafe{
            vowels_count+= ARE_VOWELS[c as usize] as usize;
        }
    }
  
    return vowels_count;
}

Although I have serious doubts about the performance & maintainability of such piece of code.

Littleton answered 22/5, 2022 at 17:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.