Cet article est la suite d’un précédent.
Configurer les séquences pour l’alignement
Après avoir ouvert et lu des fichiers, il faut maintenant lire un fichier au format FASTA, en se limitant pour l’instant à un fichier qui ne contient qu’une seule séquence, séparer la ligne initiale de commentaire (qui contient les identifiants de la séquence) des lignes suivantes (qui contiennent le texte proprement dit de la séquence), puis remettre bout à bout les lignes de texte de la séquence pour avoir une seule chaîne de caractères (le format FASTA ne tolère que des lignes de 120 caractères, ce qui est insuffisant pour des séquences réelles, et impose donc des sauts de lignes nuisibles à un alignement correct). On trouvera deux fichiers d’exemples de séquences au format FASTA à la fin de l’article précédent. Rien de bien difficile a priori, mais nous allons rencontrer les idiosyncrasies originales de Rust.
Le seul module du programme précédent qu’il faut modifier est fasta_read_seq
. Comme vu à l’article précédent, la fonction fasta_read_seq
reçoit en argument une structure de type Config
qui contient, sous forme de chaînes de caractères, les noms des deux fichiers de séquences (nous n’envisageons pas pour l’instant l’alignement multiple, qui considère plusieurs séquences). Il faut passer chacun de ces noms de fichiers à une fonction les_lignes
qui va effectuer le travail énoncé au paragraphe précédent et renvoyer, pour chaque fichier un tuple de deux éléments, ident
qui contiendra le texte de la ligne initiale de commentaire et sequence
qui contiendra le texte de la séquence dans une seule chaîne de caractères.
Rencontre avec un nouveau modèle de mémoire
Je veux imprimer, séparément, ident
et sequence
renvoyés par les_lignes
. Commençons avec ident
:
println!("{}\n", les_lignes(f1).0);
Cette instruction donne bien le résultat attendu, les_lignes(f1)
renvoie le tuple (ident, sequence)
, et les_lignes(f1).0
en extrait le premier élément, numéroté 0, que println!("{}\n", les_lignes(f1).0);
se fait un plaisir d’imprimer.
Essayons avec ident
et sequence
, il ne semble pas y avoir de différence de procédé :
println!("{}\n{}\n", les_lignes(f1).0, les_lignes(f1).1);
Cela ne marche pas. les_lignes(f1).0
a emprunté la variable f1
à fasta_read_seq
, qui ne peut plus l’utiliser lors de la seconde tentative d’invocation ! Voici le message d’erreur du compilateur :
Pour que cela marche, il faut passer les arguments par référence, ainsi :
println!("{}\n{}\n", les_lignes(&f1).0, les_lignes(&f1).1);
et la fonction les_lignes
, qui renvoie un tuple de type (String, String)
, s’écrit ainsi :
On remarque la méthode push_str
qui concatène son argument à la suite de la chaîne de l’instance qui l’invoque. La fonction read_lines
renvoie un itérateur lines
sur la lecture du fichier, ce qui évite de le charger en mémoire, et permet d’examiner les lignes une à une. Ces fonctions assez complexes sont très inspirées de la documentation officielle de Rust.
Sur le conseil d’un lecteur j’ai remplacé l’usage d’une tranche (slice) pour repérer les lignes dont le premier caractère est ">"
par le recours à la méthode texte.starts_with(">")
:
Le module complet est désormais :
Il y a encore des choses inexpliquées dans ce programme. Ce qui est bien avec Cargo et le compilateur Rust, c’est qu’ils émettent des diagnostics perspicaces qui donnent soit directement la réponse au problème, soit des pistes de recherche judicieuses qui, avec l’aide d’un manuel et de la documentation en ligne, aboutissent au résultat. Comme avec Ada, la rigueur du modèle de mémoire et du typage donne du mal pour obtenir un code qui compile, mais une fois que cela compile, la réussite de l’exécution n’est pas loin.