Sortie de Rust en version 1.27 stable

Par:
fredericmazue

lun, 25/06/2018 - 15:56

Le blog de Rust annonce la sortie de la version 1.27 stable de ce langage. Une mouture qui se distingue notamment par deux nouveautés.

La première nouveauté est l'arrivée d'un support de base du paradigme SIMD 'Single Instruction, Multiple Data'. Pour expliquer ce dont il s'agit, les créateur de Rust partenet d'une fonction :

pub fn foo(a: &[u8], b: &[u8], c: &mut [u8]) {
    for ((a, b), c) in a.iter().zip(b).zip(c) {
        *c = *a + *b;
    }
}

Cette fonction reçoit deux slices a et b en arguments, ajoute leurs éléments un à un et place le résultat dans un troisième slice c également reçu en argument. Supposons que a et b soient constitués chacun de 16 éléments. Si chacun des éléments tient dans un octet, cela signifie que chaque slices tient dans 128 bits. Avec SIMD il est possible de placer chacun des slice dans un registre 128 bits, d'ajouter les deux avec une seule instruction et de placer les 128 bits résultants dans c. Parfois un compilateur est suffisamment intelligent pour faire ce genre d'opérations 'd'autovectorisation', parfois non. De plus tous les processeurs ne proposent pas cette possibilité. Le module std::arch répond à ces problèmes en permettant d'imposer explicitement l'optimisation lorsque le compilateur ne le peut pas.

Voici deux exemples d'utilisation. Le premier, statique, lors de la compilation. Le second, dynamique, lord de l'exécution. Dans les deux cas, il s'agit d'utiliser AVX2, une fonctionnalité SIMD qui permet de faire des opérations sur 256 bit. 

#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"),
      target_feature = "avx2"))]

fn foo() {
    #[cfg(target_arch = "x86")]
    use std::arch::x86::_mm256_add_epi64;
    #[cfg(target_arch = "x86_64")]
    use std::arch::x86_64::_mm256_add_epi64; 

    unsafe {
        _mm256_add_epi64(...);
    }

Dans cette version statique, des drapeaux cfg permettent d'effectuer le bon appel à la compilation en fonction de l'architecture cible.

fn foo() {
    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
    {
        if is_x86_feature_detected!("avx2") {
            return unsafe { foo_avx2() };
        }
    } 
   foo_fallback();

Dans la version dynamique, la macro is_x86_feature_detected générera du code qui détecte si le CPU prend en charge AVX2 et, si c'est le cas, appelle la fonctions foo_avx2 fonction. Sinon, c'est un fallback non-AVX qui sera appelé.

La deuxième nouveauté est la déclaration d'un trait dans une signature, avec (dyn Trait). Il s'agit de remédier à une source de confusion du langage.

Par exemple, dans

Box<Foo>

Foo peut tout aussi bien être un trait qu'une structure. Avec la nouvelle syntaxe, l'exemple ci-dessus devient, pour indiquer que Foo est un trait

Box<dyn Foo>

Rust 1.27 voit également de nombreuses API stabilisées:

  • DoubleEndedIterator::rfind
  • DoubleEndedIterator::rfold
  • DoubleEndedIterator::try_rfold
  • Duration::from_micros
  • Duration::from_nanos
  • Duration::subsec_micros
  • Duration::subsec_millis
  • HashMap::remove_entry
  • Iterator::try_fold
  • Iterator::try_for_each
  • NonNull::cast
  • Option::filter
  • String::replace_range
  • Take::set_limit
  • hint::unreachable_unchecked
  • os::unix::process::parent_id
  • process::id
  • ptr::swap_nonoverlapping
  • slice::rsplit_mut
  • slice::rsplit
  • slice::swap_with_slice

Découvrez toutes les nouveautés de Rust 1.27 dans sa note de version.