Repository: WorksApplications/sudachi.rs (language: python)

Issue:
Fix clippy warnings

Fix clippy warnings (breaking changes)
partial fix for #40.

Fix clippy warnings (rest of #263).

Contains some breaking changes:

- rm unused `BAKED_DICTIONARY_BYTES`
- rm unused `SplitIter`
- rename `StringNumber.to_string` -> `get_normalized`
- change some func args more general (&String -> &str, &Vec -> &[])
- `RegexError.cause` is boxed
- `AnalyzeNonSplitted` and `AnalyzeSplitted` takes boxed `SudachiOutput`


Discussion / hints:

Fix patch:
diff --git a/python/src/build.rs b/python/src/build.rs
index 8fee1ff8..986144b5 100644
--- a/python/src/build.rs
+++ b/python/src/build.rs
@@ -14,18 +14,21 @@
  *  limitations under the License.
  */
 
-use crate::dictionary::get_default_resource_dir;
-use crate::errors;
-use pyo3::prelude::*;
-use pyo3::types::{PyBytes, PyList, PyString, PyTuple, PyType};
 use std::fs::{File, OpenOptions};
 use std::io::BufWriter;
 use std::path::Path;
+
+use pyo3::prelude::*;
+use pyo3::types::{PyBytes, PyList, PyString, PyTuple, PyType};
+
 use sudachi::analysis::stateless_tokenizer::DictionaryAccess;
 use sudachi::config::Config;
 use sudachi::dic::build::{DataSource, DictBuilder};
 use sudachi::dic::dictionary::JapaneseDictionary;
 
+use crate::dictionary::get_default_resource_dir;
+use crate::errors;
+
 pub fn register_functions(m: &Bound<PyModule>) -> PyResult<()> {
     m.add_function(wrap_pyfunction!(build_system_dic, m)?)?;
     m.add_function(wrap_pyfunction!(build_user_dic, m)?)?;
@@ -80,7 +83,9 @@ fn build_system_dic<'py>(
     description: Option<&str>,
 ) -> PyResult<Bound<'py, PyList>> {
     let mut builder = DictBuilder::new_system();
-    description.map(|d| builder.set_description(d));
+    if let Some(d) = description {
+        builder.set_description(d)
+    }
 
     let matrix_path = resolve_as_pypathstr(py, matrix)?;
     let matrix_src = as_data_source(matrix_path.as_ref(), matrix)?;
@@ -138,7 +143,9 @@ fn build_user_dic<'py>(
     };
 
     let mut builder = DictBuilder::new_user(&system_dic);
-    description.map(|d| builder.set_description(d));
+    if let Some(d) = description {
+        builder.set_description(d)
+    }
 
     for f in lex.iter() {
         let lex_path = resolve_as_pypathstr(py, &f)?;
diff --git a/python/src/dictionary.rs b/python/src/dictionary.rs
index 30fbeda6..8239d42b 100644
--- a/python/src/dictionary.rs
+++ b/python/src/dictionary.rs
@@ -14,17 +14,18 @@
  *  limitations under the License.
  */
 
-use pyo3::prelude::*;
-use pyo3::types::{PySet, PyString, PyTuple};
 use std::convert::TryFrom;
 use std::fmt::Write;
 use std::ops::Deref;
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
 use std::sync::Arc;
-use sudachi::analysis::Mode;
+
+use pyo3::prelude::*;
+use pyo3::types::{PySet, PyString, PyTuple};
 
 use sudachi::analysis::stateless_tokenizer::DictionaryAccess;
+use sudachi::analysis::Mode;
 use sudachi::config::{Config, ConfigBuilder, SurfaceProjection};
 use sudachi::dic::dictionary::JapaneseDictionary;
 use sudachi::dic::grammar::Grammar;
@@ -447,7 +448,7 @@ fn config_repr(cfg: &Config) -> Result<String, std::fmt::Error> {
     Ok(result)
 }
 
-pub(crate) fn extract_mode<'py>(mode: &Bound<'py, PyAny>) -> PyResult<Mode> {
+pub(crate) fn extract_mode(mode: &Bound<'_, PyAny>) -> PyResult<Mode> {
     if mode.is_instance_of::<PyString>() {
         errors::wrap(Mode::from_str(mode.str()?.to_str()?))
     } else if mode.is_instance_of::<PySplitMode>() {
@@ -471,7 +472,7 @@ fn read_config(config_opt: &Bound<PyAny>) -> PyResult<ConfigBuilder> {
         let config_pystr = config_opt.str()?;
         let config_str = config_pystr.to_str()?.trim();
         // looks like json
-        if config_str.starts_with("{") && config_str.ends_with("}") {
+        if config_str.starts_with('{') && config_str.ends_with('}') {
             let result = ConfigBuilder::from_bytes(config_str.as_bytes());
             return errors::wrap(result);
         }
diff --git a/python/src/errors.rs b/python/src/errors.rs
index 6fdd0e97..a049c17d 100644
--- a/python/src/errors.rs
+++ b/python/src/errors.rs
@@ -14,10 +14,11 @@
  *  limitations under the License.
  */
 
+use std::fmt::{Debug, Display};
+
 use pyo3::exceptions::PyDeprecationWarning;
 use pyo3::prelude::*;
 use pyo3::{import_exception, PyResult};
-use std::fmt::{Debug, Display};
 
 // Sudachi exception class is defined in Python
 import_exception!(sudachipy.errors, SudachiError);
diff --git a/python/src/morpheme.rs b/python/src/morpheme.rs
index 3cf1bc5d..80853b10 100644
--- a/python/src/morpheme.rs
+++ b/python/src/morpheme.rs
@@ -163,7 +163,7 @@ impl PyMorphemeListWrapper {
         for (i, m) in list.iter().enumerate() {
             result.push_str(m.surface().deref());
             if i + 1 != nmorphs {
-                result.push_str(" ");
+                result.push(' ');
             }
         }
         PyString::new_bound(py, result.as_str())
@@ -196,7 +196,7 @@ impl PyMorphemeListWrapper {
     }
 
     fn __bool__(&self, py: Python) -> bool {
-        self.internal(py).len() != 0
+        !self.internal(py).is_empty()
     }
 }
 
diff --git a/python/src/pos_matcher.rs b/python/src/pos_matcher.rs
index 352d21c1..b8648517 100644
--- a/python/src/pos_matcher.rs
+++ b/python/src/pos_matcher.rs
@@ -54,7 +54,7 @@ impl PyPosMatcher {
     fn create_from_fn(dic: &Arc<PyDicData>, func: &Bound<PyAny>, py: Python) -> PyResult<Self> {
         let mut data = Vec::new();
         for (pos_id, pos) in dic.pos.iter().enumerate() {
-            let args = PyTuple::new_bound(py, &[pos]);
+            let args = PyTuple::new_bound(py, [pos]);
             if func.call1(args)?.downcast::<PyBool>()?.is_true() {
                 data.push(pos_id as u16);
             }
@@ -198,7 +198,6 @@ impl PyPosMatcher {
         let max_id = self.dic.pos.len();
         // map -> filter chain is needed to handle exactly u16::MAX POS entries
         let values = (0..max_id)
-            .into_iter()
             .map(|x| x as u16)
             .filter(|id| !self.matcher.matches_id(*id));
         let matcher = PosMatcher::new(values);
diff --git a/python/src/pretokenizer.rs b/python/src/pretokenizer.rs
index 3d72daab..8faab5e5 100644
--- a/python/src/pretokenizer.rs
+++ b/python/src/pretokenizer.rs
@@ -14,21 +14,23 @@
  *  limitations under the License.
  */
 
-use crate::dictionary::PyDicData;
-use crate::errors;
-use crate::morpheme::{PyMorphemeList, PyMorphemeListWrapper, PyProjector};
+use std::cell::RefCell;
+use std::sync::Arc;
+
 use pyo3::intern;
 use pyo3::prelude::*;
 use pyo3::sync::GILOnceCell;
 use pyo3::types::{PyList, PySlice, PyTuple, PyType};
-use std::cell::RefCell;
-use std::sync::Arc;
+use thread_local::ThreadLocal;
 
-use crate::projection::MorphemeProjection;
 use sudachi::analysis::stateful_tokenizer::StatefulTokenizer;
 use sudachi::dic::subset::InfoSubset;
 use sudachi::prelude::Mode;
-use thread_local::ThreadLocal;
+
+use crate::dictionary::PyDicData;
+use crate::errors;
+use crate::morpheme::{PyMorphemeList, PyMorphemeListWrapper, PyProjector};
+use crate::projection::MorphemeProjection;
 
 /// This struct perform actual tokenization
 /// There should be at most one instance per thread of execution
@@ -152,7 +154,7 @@ impl PyPretokenizer {
             }
             Some(h) => {
                 let mrp: &Bound<PyAny> = morphs.bind(py);
-                let args = PyTuple::new_bound(py, &[index, string, mrp]);
+                let args = PyTuple::new_bound(py, [index, string, mrp]);
                 h.bind(py).call1(args)
             }
         }
diff --git a/python/src/projection.rs b/python/src/projection.rs
index 97d2c557..bca184ec 100644
--- a/python/src/projection.rs
+++ b/python/src/projection.rs
@@ -14,20 +14,23 @@
  *  limitations under the License.
  */
 
-use crate::dictionary::PyDicData;
-use crate::errors;
-use crate::morpheme::PyProjector;
-use pyo3::prelude::*;
-use pyo3::types::PyString;
-use pyo3::{PyResult, Python};
 use std::convert::TryFrom;
 use std::ops::Deref;
 use std::sync::Arc;
+
+use pyo3::prelude::*;
+use pyo3::types::PyString;
+use pyo3::{PyResult, Python};
+
 use sudachi::analysis::stateless_tokenizer::DictionaryAccess;
 use sudachi::config::SurfaceProjection;
 use sudachi::pos::PosMatcher;
 use sudachi::prelude::Morpheme;
 
+use crate::dictionary::PyDicData;
+use crate::errors;
+use crate::morpheme::PyProjector;
+
 pub(crate) trait MorphemeProjection {
     fn project<'py>(&self, m: &Morpheme<Arc<PyDicData>>, py: Python<'py>) -> Bound<'py, PyString>;
 }
@@ -114,9 +117,8 @@ impl MorphemeProjection for NormalizedNouns {
 }
 
 fn conjugating_matcher<D: DictionaryAccess>(dic: &D) -> PosMatcher {
-    make_matcher(dic, |pos| match pos[0].deref() {
-        "動詞" | "形容詞" | "助動詞" => true,
-        _ => false,
+    make_matcher(dic, |pos| {
+        matches!(pos[0].deref(), "動詞" | "形容詞" | "助動詞")
     })
 }
 
diff --git a/python/src/tokenizer.rs b/python/src/tokenizer.rs
index a5026b9b..6dcdc7ce 100644
--- a/python/src/tokenizer.rs
+++ b/python/src/tokenizer.rs
@@ -21,7 +21,6 @@ use std::sync::Arc;
 use pyo3::prelude::*;
 
 use sudachi::analysis::stateful_tokenizer::StatefulTokenizer;
-
 use sudachi::dic::subset::InfoSubset;
 use sudachi::prelude::*;
 
@@ -157,7 +156,7 @@ impl PyTokenizer {
             None => None,
             Some(m) => Some(extract_mode(m)?),
         };
-        let default_mode = mode.map(|m| self.tokenizer.set_mode(m.into()));
+        let default_mode = mode.map(|m| self.tokenizer.set_mode(m));
         let mut tokenizer = scopeguard::guard(&mut self.tokenizer, |t| {
             default_mode.map(|m| t.set_mode(m));
         });
diff --git a/sudachi-cli/src/build.rs b/sudachi-cli/src/build.rs
index dbb03444..6010c54d 100644
--- a/sudachi-cli/src/build.rs
+++ b/sudachi-cli/src/build.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -14,12 +14,12 @@
  *  limitations under the License.
  */
 
-use memmap2::Mmap;
 use std::fs::{File, OpenOptions};
 use std::io::{BufWriter, Write};
 use std::path::{Path, PathBuf};
 
 use clap::{Args, Subcommand};
+use memmap2::Mmap;
 
 use sudachi::analysis::stateless_tokenizer::DictionaryAccess;
 use sudachi::config::Config;
diff --git a/sudachi-cli/src/main.rs b/sudachi-cli/src/main.rs
index 69aabc7b..3d825d15 100644
--- a/sudachi-cli/src/main.rs
+++ b/sudachi-cli/src/main.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -34,9 +34,10 @@ use sudachi::prelude::*;
 #[cfg(feature = "bake_dictionary")]
 const BAKED_DICTIONARY_BYTES: &[u8] = include_bytes!(env!("SUDACHI_DICT_PATH"));
 
-#[derive(Clone, Debug, Eq, PartialEq)]
+#[derive(Clone, Debug, Eq, PartialEq, Default)]
 pub enum SentenceSplitMode {
     /// Do both sentence splitting and analysis
+    #[default]
     Default,
     /// Do only sentence splitting and not analysis
     Only,
@@ -44,12 +45,6 @@ pub enum SentenceSplitMode {
     None,
 }
 
-impl Default for SentenceSplitMode {
-    fn default() -> Self {
-        SentenceSplitMode::Default
-    }
-}
-
 impl FromStr for SentenceSplitMode {
     type Err = &'static str;
 
@@ -156,7 +151,7 @@ fn main() {
     // output: stdout or file
     let inner_writer: Box<dyn Write> = match &args.output_file {
         Some(output_path) => Box::new(
-            File::create(&output_path)
+            File::create(output_path)
                 .unwrap_or_else(|_| panic!("Failed to open output file {:?}", &output_path)),
         ),
         None => Box::new(io::stdout()),
@@ -207,10 +202,10 @@ fn strip_eol(data: &str) -> &str {
     let mut bytes = data.as_bytes();
     let mut len = bytes.len();
     if len > 1 && bytes[len - 1] == b'\n' {
-        len = len - 1;
+        len -= 1;
         bytes = &bytes[..len];
         if len > 1 && bytes[len - 1] == b'\r' {
-            len = len - 1;
+            len -= 1;
             bytes = &bytes[..len];
         }
     }
diff --git a/sudachi-cli/src/output.rs b/sudachi-cli/src/output.rs
index d242ce53..94871dab 100644
--- a/sudachi-cli/src/output.rs
+++ b/sudachi-cli/src/output.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@ impl Wakachi {
 
 impl<T: DictionaryAccess> SudachiOutput<T> for Wakachi {
     fn write(&self, writer: &mut Writer, morphemes: &MorphemeList<T>) -> SudachiResult<()> {
-        if morphemes.len() == 0 {
+        if morphemes.is_empty() {
             writer.write_all(b"\n")?;
             return Ok(());
         }
diff --git a/sudachi/src/analysis/created.rs b/sudachi/src/analysis/created.rs
index 87313bbe..2bce363f 100644
--- a/sudachi/src/analysis/created.rs
+++ b/sudachi/src/analysis/created.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -40,7 +40,7 @@ impl CreatedWords {
     const MAX_SHIFT: Carrier = CreatedWords::MAX_VALUE - 1;
 
     pub fn empty() -> CreatedWords {
-        return Default::default();
+        Default::default()
     }
 
     pub fn single<Pos: Into<i64>>(length: Pos) -> CreatedWords {
@@ -55,7 +55,7 @@ impl CreatedWords {
     #[must_use]
     pub fn add_word<P: Into<i64>>(&self, length: P) -> CreatedWords {
         let mask = CreatedWords::single(length);
-        return self.add(mask);
+        self.add(mask)
     }
 
     #[must_use]
@@ -67,21 +67,19 @@ impl CreatedWords {
         let mask = CreatedWords::single(length);
         if (self.0 & mask.0) == 0 {
             HasWord::No
+        } else if length.into() >= CreatedWords::MAX_VALUE as _ {
+            HasWord::Maybe
         } else {
-            if length.into() >= CreatedWords::MAX_VALUE as _ {
-                HasWord::Maybe
-            } else {
-                HasWord::Yes
-            }
+            HasWord::Yes
         }
     }
 
     pub fn is_empty(&self) -> bool {
-        return self.0 == 0;
+        self.0 == 0
     }
 
     pub fn not_empty(&self) -> bool {
-        return !self.is_empty();
+        !self.is_empty()
     }
 }
 
diff --git a/sudachi/src/analysis/lattice.rs b/sudachi/src/analysis/lattice.rs
index 7d1c4a97..b7bd227a 100644
--- a/sudachi/src/analysis/lattice.rs
+++ b/sudachi/src/analysis/lattice.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -70,6 +70,7 @@ impl VNode {
 /// the size of vectors never shrink.
 /// You must use the size parameter to check the current size and never
 /// access vectors after the end.
+#[derive(Default)]
 pub struct Lattice {
     ends: Vec<Vec<VNode>>,
     ends_full: Vec<Vec<Node>>,
@@ -78,18 +79,6 @@ pub struct Lattice {
     size: usize,
 }
 
-impl Default for Lattice {
-    fn default() -> Self {
-        Lattice {
-            ends: Vec::new(),
-            ends_full: Vec::new(),
-            indices: Vec::new(),
-            eos: None,
-            size: 0,
-        }
-    }
-}
-
 impl Lattice {
     fn reset_vec<T>(data: &mut Vec<Vec<T>>, target: usize) {
         for v in data.iter_mut() {
@@ -240,10 +229,7 @@ impl Lattice {
         let mut dump_idx = 0;
 
         for boundary in (0..self.indices.len()).rev() {
-            let nodes = &self.ends_full[boundary];
-
-            for node_idx in 0..nodes.len() {
-                let r_node = &nodes[node_idx];
+            for r_node in &self.ends_full[boundary] {
                 let (surface, pos) = if r_node.is_special_node() {
                     ("(null)", PosData::Bos)
                 } else if r_node.is_oov() {
@@ -282,7 +268,7 @@ impl Lattice {
                     write!(out, " {}", connect_cost)?;
                 }
 
-                write!(out, "\n")?;
+                writeln!(out)?;
 
                 dump_idx += 1;
             }
diff --git a/sudachi/src/analysis/mlist.rs b/sudachi/src/analysis/mlist.rs
index 2d1a76f0..2e80cb56 100644
--- a/sudachi/src/analysis/mlist.rs
+++ b/sudachi/src/analysis/mlist.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -93,11 +93,7 @@ impl<T: DictionaryAccess> MorphemeList<T> {
         match self.input.try_borrow_mut() {
             Ok(mut i) => {
                 let mref = i.deref_mut();
-                analyzer.swap_result(
-                    &mut mref.input,
-                    &mut self.nodes.mut_data(),
-                    &mut mref.subset,
-                );
+                analyzer.swap_result(&mut mref.input, self.nodes.mut_data(), &mut mref.subset);
                 Ok(())
             }
             Err(_) => Err(SudachiError::MorphemeListBorrowed),
diff --git a/sudachi/src/analysis/morpheme.rs b/sudachi/src/analysis/morpheme.rs
index c412e66b..39929c63 100644
--- a/sudachi/src/analysis/morpheme.rs
+++ b/sudachi/src/analysis/morpheme.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -91,21 +91,21 @@ impl<'a, T: DictionaryAccess> Morpheme<'a, T> {
     ///
     /// "Dictionary form" means a word's lemma and "終止形" in Japanese.
     pub fn dictionary_form(&self) -> &str {
-        &self.get_word_info().dictionary_form()
+        self.get_word_info().dictionary_form()
     }
 
     /// Returns the normalized form of morpheme
     ///
     /// This method returns the form normalizing inconsistent spellings and inflected forms
     pub fn normalized_form(&self) -> &str {
-        &self.get_word_info().normalized_form()
+        self.get_word_info().normalized_form()
     }
 
     /// Returns the reading form of morpheme.
     ///
     /// Returns Japanese syllabaries 'フリガナ' in katakana.
     pub fn reading_form(&self) -> &str {
-        &self.get_word_info().reading_form()
+        self.get_word_info().reading_form()
     }
 
     /// Returns if this morpheme is out of vocabulary
@@ -131,7 +131,7 @@ impl<'a, T: DictionaryAccess> Morpheme<'a, T> {
     }
 
     pub fn synonym_group_ids(&self) -> &[u32] {
-        &self.get_word_info().synonym_group_ids()
+        self.get_word_info().synonym_group_ids()
     }
 
     pub fn get_word_info(&self) -> &WordInfo {
diff --git a/sudachi/src/analysis/node.rs b/sudachi/src/analysis/node.rs
index 2e840080..fa2537d3 100644
--- a/sudachi/src/analysis/node.rs
+++ b/sudachi/src/analysis/node.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -186,8 +186,8 @@ impl ResultNode {
         text: &'a InputBuffer,
     ) -> NodeSplitIterator<'a> {
         let splits: &[WordId] = match mode {
-            Mode::A => &self.word_info.a_unit_split(),
-            Mode::B => &self.word_info.b_unit_split(),
+            Mode::A => self.word_info.a_unit_split(),
+            Mode::B => self.word_info.b_unit_split(),
             Mode::C => panic!("splitting Node with Mode::C is not supported"),
         };
 
diff --git a/sudachi/src/analysis/stateful_tokenizer.rs b/sudachi/src/analysis/stateful_tokenizer.rs
index fa69402e..ec7e8db5 100644
--- a/sudachi/src/analysis/stateful_tokenizer.rs
+++ b/sudachi/src/analysis/stateful_tokenizer.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -88,7 +88,7 @@ impl<D: DictionaryAccess> StatefulTokenizer<D> {
 
     /// Return current analysis mode
     pub fn mode(&self) -> Mode {
-        return self.mode;
+        self.mode
     }
 
     /// Analyzer will read only following [`WordInfo`] field subset
@@ -105,7 +105,9 @@ impl<D: DictionaryAccess> StatefulTokenizer<D> {
     /// Prepare StatefulTokenizer for the next data.
     /// Data must be written in the returned reference.
     pub fn reset(&mut self) -> &mut String {
-        self.top_path.as_mut().map(|p| p.clear());
+        if let Some(p) = self.top_path.as_mut() {
+            p.clear()
+        }
         self.oov.clear();
         self.input.reset()
     }
@@ -169,7 +171,7 @@ impl<D: DictionaryAccess> StatefulTokenizer<D> {
     /// Resolve the path (as ResultNodes) with the smallest cost
     fn resolve_best_path(&mut self) -> SudachiResult<Vec<ResultNode>> {
         let lex = self.dictionary.lexicon();
-        let mut path = std::mem::replace(&mut self.top_path, None).unwrap_or_else(|| Vec::new());
+        let mut path = self.top_path.take().unwrap_or_default();
         self.lattice.fill_top_path(&mut self.top_path_ids);
         self.top_path_ids.reverse();
         for pid in self.top_path_ids.drain(..) {
diff --git a/sudachi/src/analysis/stateless_tokenizer.rs b/sudachi/src/analysis/stateless_tokenizer.rs
index 5670d217..669d7720 100644
--- a/sudachi/src/analysis/stateless_tokenizer.rs
+++ b/sudachi/src/analysis/stateless_tokenizer.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -133,7 +133,7 @@ pub(super) fn split_path<T: DictionaryAccess + ?Sized>(
 }
 
 pub(super) fn dump_path(path: &Vec<ResultNode>) {
-    for (i, node) in (&path).iter().enumerate() {
+    for (i, node) in path.iter().enumerate() {
         println!("{}: {}", i, node);
     }
 }
diff --git a/sudachi/src/config.rs b/sudachi/src/config.rs
index d67f12e9..d65dfb1c 100644
--- a/sudachi/src/config.rs
+++ b/sudachi/src/config.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -61,9 +61,9 @@ struct PathResolver {
 
 impl PathResolver {
     fn with_capacity(capacity: usize) -> PathResolver {
-        return PathResolver {
+        PathResolver {
             roots: Vec::with_capacity(capacity),
-        };
+        }
     }
 
     fn add<P: Into<PathBuf>>(&mut self, path: P) {
@@ -72,7 +72,7 @@ impl PathResolver {
 
     fn contains<P: AsRef<Path>>(&self, path: P) -> bool {
         let query = path.as_ref();
-        return self.roots.iter().find(|p| p.as_path() == query).is_some();
+        return self.roots.iter().any(|p| p.as_path() == query);
     }
 
     pub fn first_existing<P: AsRef<Path> + Clone>(&self, path: P) -> Option<PathBuf> {
@@ -96,13 +96,14 @@ impl PathResolver {
     }
 
     pub fn roots(&self) -> &[PathBuf] {
-        return &self.roots;
+        &self.roots
     }
 }
 
-#[derive(Deserialize, Clone, Copy, Debug, Eq, PartialEq)]
+#[derive(Deserialize, Clone, Copy, Debug, Eq, PartialEq, Default)]
 #[serde(rename_all = "snake_case")]
 pub enum SurfaceProjection {
+    #[default]
     Surface,
     Normalized,
     Reading,
@@ -112,12 +113,6 @@ pub enum SurfaceProjection {
     NormalizedNouns,
 }
 
-impl Default for SurfaceProjection {
-    fn default() -> Self {
-        SurfaceProjection::Surface
-    }
-}
-
 impl SurfaceProjection {
     /// Return required InfoSubset for the current projection type
     pub fn required_subset(&self) -> InfoSubset {
@@ -293,13 +288,13 @@ impl ConfigBuilder {
         Config {
             resolver,
             system_dict: self.systemDict,
-            user_dicts: self.userDict.unwrap_or_else(|| Vec::new()),
+            user_dicts: self.userDict.unwrap_or_default(),
             character_definition_file,
 
-            connection_cost_plugins: self.connectionCostPlugin.unwrap_or(Vec::new()),
-            input_text_plugins: self.inputTextPlugin.unwrap_or(Vec::new()),
-            oov_provider_plugins: self.oovProviderPlugin.unwrap_or(Vec::new()),
-            path_rewrite_plugins: self.pathRewritePlugin.unwrap_or(Vec::new()),
+            connection_cost_plugins: self.connectionCostPlugin.unwrap_or_default(),
+            input_text_plugins: self.inputTextPlugin.unwrap_or_default(),
+            oov_provider_plugins: self.oovProviderPlugin.unwrap_or_default(),
+            path_rewrite_plugins: self.pathRewritePlugin.unwrap_or_default(),
             projection: self.projection.unwrap_or(SurfaceProjection::Surface),
         }
     }
@@ -423,7 +418,7 @@ impl Config {
         }
 
         // Report an error
-        return Err(self.resolver.resolution_failure(&file_path));
+        Err(self.resolver.resolution_failure(&file_path))
     }
 
     pub fn resolved_system_dict(&self) -> Result<PathBuf, ConfigError> {
diff --git a/sudachi/src/dic/build/conn.rs b/sudachi/src/dic/build/conn.rs
index d73cd17e..338e4f59 100644
--- a/sudachi/src/dic/build/conn.rs
+++ b/sudachi/src/dic/build/conn.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -138,7 +138,7 @@ impl ConnBuffer {
     }
 
     fn parse_header(&mut self) -> DicWriteResult<(i16, i16)> {
-        let mut items = SPLIT_REGEX.splitn(&self.line.trim(), 2);
+        let mut items = SPLIT_REGEX.splitn(self.line.trim(), 2);
         // TODO: fix get_next error message
         let left = it_next(&self.line, &mut items, "left_num", parse_i16)?;
         let right = it_next(&self.line, &mut items, "right_num", parse_i16)?;
@@ -146,7 +146,7 @@ impl ConnBuffer {
     }
 
     fn parse_line(&mut self) -> DicWriteResult<()> {
-        let mut items = SPLIT_REGEX.splitn(&self.line.trim(), 3);
+        let mut items = SPLIT_REGEX.splitn(self.line.trim(), 3);
         let left = it_next(&self.line, &mut items, "left", parse_i16)?;
         let right = it_next(&self.line, &mut items, "right", parse_i16)?;
         let cost = it_next(&self.line, &mut items, "cost", parse_i16)?;
@@ -164,12 +164,12 @@ impl ConnBuffer {
 }
 
 fn num_error<T>(part: &'static str, value: i16) -> SudachiResult<T> {
-    return Err(DicBuildError {
+    Err(DicBuildError {
         file: "<connection>".to_owned(),
         line: 0,
         cause: BuildFailure::InvalidConnSize(part, value),
     }
-    .into());
+    .into())
 }
 
 #[cfg(test)]
diff --git a/sudachi/src/dic/build/error.rs b/sudachi/src/dic/build/error.rs
index 169604f7..487adf98 100644
--- a/sudachi/src/dic/build/error.rs
+++ b/sudachi/src/dic/build/error.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -95,20 +95,12 @@ pub enum BuildFailure {
     TrieBuildFailure,
 }
 
+#[derive(Default)]
 pub(crate) struct DicCompilationCtx {
     name: String,
     line: usize,
 }
 
-impl Default for DicCompilationCtx {
-    fn default() -> Self {
-        DicCompilationCtx {
-            name: Default::default(),
-            line: Default::default(),
-        }
-    }
-}
-
 impl DicCompilationCtx {
     pub fn memory() -> Self {
         DicCompilationCtx {
diff --git a/sudachi/src/dic/build/lexicon.rs b/sudachi/src/dic/build/lexicon.rs
index e0f15e07..49e44963 100644
--- a/sudachi/src/dic/build/lexicon.rs
+++ b/sudachi/src/dic/build/lexicon.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -146,7 +146,7 @@ pub(crate) trait SplitUnitResolver {
                 surface,
                 pos,
                 reading,
-            } => self.resolve_inline(&surface, *pos, reading.as_deref()),
+            } => self.resolve_inline(surface, *pos, reading.as_deref()),
         }
     }
 
@@ -206,7 +206,7 @@ impl RawLexiconEntry {
     ) -> DicWriteResult<usize> {
         let mut size = 0;
 
-        size += u16w.write(w, &self.headword())?; // surface of WordInfo
+        size += u16w.write(w, self.headword())?; // surface of WordInfo
         size += u16w.write_len(w, self.surface.len())?; // surface for trie
         w.write_all(&self.pos.to_le_bytes())?;
         size += 2;
@@ -297,17 +297,21 @@ impl LexiconReader {
             .trim(Trim::None)
             .flexible(true)
             .from_reader(data);
-        let mut record = StringRecord::new();
         let mut nread = 0;
-        while reader.read_record(&mut record).map_err(|e| {
-            let line = e.position().map_or(0, |p| p.line());
-            self.ctx.set_line(line as usize);
-            self.ctx.to_sudachi_err(BuildFailure::CsvError(e))
-        })? {
-            let line = record.position().map_or(0, |p| p.line()) as usize;
-            self.ctx.set_line(line);
-            self.read_record(&record)?;
-            nread += 1;
+        for record in reader.records() {
+            match record {
+                Ok(r) => {
+                    let line = r.position().map_or(0, |p| p.line()) as usize;
+                    self.ctx.set_line(line);
+                    self.read_record(&r)?;
+                    nread += 1;
+                }
+                Err(e) => {
+                    let line = e.position().map_or(0, |p| p.line()) as usize;
+                    self.ctx.set_line(line);
+                    return Err(self.ctx.to_sudachi_err(BuildFailure::CsvError(e)));
+                }
+            }
         }
         Ok(nread)
     }
@@ -344,12 +348,10 @@ impl LexiconReader {
 
         let pos = rec.ctx.transform(self.pos_of([p1, p2, p3, p4, p5, p6]))?;
 
-        if splitting == Mode::A {
-            if !split_a.is_empty() || !split_b.is_empty() {
-                return rec.ctx.err(BuildFailure::InvalidSplit(
-                    "A-mode tokens can't have splits".to_owned(),
-                ));
-            }
+        if splitting == Mode::A && (!split_a.is_empty() || !split_b.is_empty()) {
+            return rec.ctx.err(BuildFailure::InvalidSplit(
+                "A-mode tokens can't have splits".to_owned(),
+            ));
         }
 
         self.unresolved += resolve_a + resolve_b;
@@ -497,7 +499,7 @@ impl LexiconReader {
         if WORD_ID_LITERAL.is_match(data) {
             Ok(SplitUnit::Ref(parse_wordid(data)?))
         } else {
-            let mut iter = data.splitn(8, ",");
+            let mut iter = data.splitn(8, ',');
             let surface = it_next(data, &mut iter, "(1) surface", unescape)?;
             let p1 = it_next(data, &mut iter, "(2) pos-1", unescape_cow)?;
             let p2 = it_next(data, &mut iter, "(3) pos-2", unescape_cow)?;
@@ -541,8 +543,7 @@ impl LexiconReader {
         resolver: &R,
     ) -> Result<usize, (String, usize)> {
         let mut total = 0;
-        let mut line: usize = 0;
-        for e in self.entries.iter_mut() {
+        for (line, e) in self.entries.iter_mut().enumerate() {
             for s in e.splits_a.iter_mut() {
                 match Self::resolve_split(s, resolver) {
                     Some(val) => total += val,
@@ -569,7 +570,6 @@ impl LexiconReader {
                     }
                 }
             }
-            line += 1;
         }
         Ok(total)
     }
diff --git a/sudachi/src/dic/build/mod.rs b/sudachi/src/dic/build/mod.rs
index 2cd1c49a..dbbdda1f 100644
--- a/sudachi/src/dic/build/mod.rs
+++ b/sudachi/src/dic/build/mod.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -109,15 +109,15 @@ impl DictionaryAccess for NoDic {
     }
 
     fn input_text_plugins(&self) -> &[Box<dyn InputTextPlugin + Sync + Send>] {
-        return &[];
+        &[]
     }
 
     fn oov_provider_plugins(&self) -> &[Box<dyn OovProviderPlugin + Sync + Send>] {
-        return &[];
+        &[]
     }
 
     fn path_rewrite_plugins(&self) -> &[Box<dyn PathRewritePlugin + Sync + Send>] {
-        return &[];
+        &[]
     }
 }
 
@@ -305,7 +305,7 @@ impl<D: DictionaryAccess> DictBuilder<D> {
     }
 
     /// this function must only be used in resolve_impl
-    fn unsafe_make_resolver<'a, 'b>(&'a self) -> RawDictResolver<'b> {
+    fn unsafe_make_resolver<'a>(&self) -> RawDictResolver<'a> {
         let resolver = RawDictResolver::new(self.lexicon.entries(), self.user);
         // resolver borrows parts of entries, but it does not touch splits
         // resolve function only modifies splits
diff --git a/sudachi/src/dic/build/parse.rs b/sudachi/src/dic/build/parse.rs
index ecf4d32e..d39e16b2 100644
--- a/sudachi/src/dic/build/parse.rs
+++ b/sudachi/src/dic/build/parse.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -95,8 +95,8 @@ pub(crate) fn parse_dic_form(data: &str) -> DicWriteResult<WordId> {
 
 #[inline]
 pub(crate) fn parse_wordid(data: &str) -> DicWriteResult<WordId> {
-    if data.starts_with("U") {
-        let wid = parse_wordid_raw(&data[1..]);
+    if let Some(stripped) = data.strip_prefix('U') {
+        let wid = parse_wordid_raw(stripped);
         wid.map(|w| WordId::new(1, w.word()))
     } else {
         parse_wordid_raw(data)
@@ -143,7 +143,7 @@ where
 {
     let mut result = Vec::with_capacity(4);
 
-    for part in data.split("/") {
+    for part in data.split('/') {
         result.push(f(part)?);
     }
 
@@ -179,7 +179,7 @@ pub(crate) fn unescape_cow(data: &str) -> DicWriteResult<Cow<str>> {
     if !UNICODE_LITERAL.is_match(data) {
         Ok(Cow::Borrowed(data))
     } else {
-        unescape_slow(data).map(|s| Cow::Owned(s))
+        unescape_slow(data).map(Cow::Owned)
     }
 }
 
diff --git a/sudachi/src/dic/build/primitives.rs b/sudachi/src/dic/build/primitives.rs
index d1b83c9e..2ee91e69 100644
--- a/sudachi/src/dic/build/primitives.rs
+++ b/sudachi/src/dic/build/primitives.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -45,7 +45,7 @@ impl Utf16Writer {
             w.write_all(&[length as u8])?;
             1
         } else {
-            let b0 = (length as u8) & 0xff;
+            let b0 = length as u8;
             let b1 = ((length >> 8) as u8) | 0x80;
             w.write_all(&[b1, b0])?;
             2
diff --git a/sudachi/src/dic/build/resolve.rs b/sudachi/src/dic/build/resolve.rs
index 3160bbab..19d4e9be 100644
--- a/sudachi/src/dic/build/resolve.rs
+++ b/sudachi/src/dic/build/resolve.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -23,18 +23,20 @@ use crate::error::SudachiResult;
 use crate::util::fxhash::FxBuildHasher;
 use std::collections::HashMap;
 
+// HashMap from surface to (pos_id, reading_form, word-id)s
+type ResolutionCandidateMap<T> = HashMap<T, Vec<(u16, Option<T>, WordId)>, FxBuildHasher>;
+
 /// We can't use trie to resolve splits because it is possible that refs are not in trie
 /// This resolver has to be owning because the dictionary content is lazily loaded and transient
 pub struct BinDictResolver {
-    index: HashMap<String, Vec<(u16, Option<String>, WordId)>, FxBuildHasher>,
+    index: ResolutionCandidateMap<String>,
 }
 
 impl BinDictResolver {
     pub fn new<D: DictionaryAccess>(dict: D) -> SudachiResult<Self> {
         let lex = dict.lexicon();
         let size = lex.size();
-        let mut index: HashMap<String, Vec<(u16, Option<String>, WordId)>, FxBuildHasher> =
-            HashMap::default();
+        let mut index: ResolutionCandidateMap<String> = HashMap::default();
         for id in 0..size {
             let wid = WordId::new(0, id);
             let winfo: WordInfoData = lex
@@ -77,13 +79,12 @@ impl SplitUnitResolver for BinDictResolver {
 }
 
 pub struct RawDictResolver<'a> {
-    data: HashMap<&'a str, Vec<(u16, Option<&'a str>, WordId)>, FxBuildHasher>,
+    data: ResolutionCandidateMap<&'a str>,
 }
 
 impl<'a> RawDictResolver<'a> {
     pub(crate) fn new(entries: &'a [RawLexiconEntry], user: bool) -> Self {
-        let mut data: HashMap<&'a str, Vec<(u16, Option<&'a str>, WordId)>, FxBuildHasher> =
-            HashMap::default();
+        let mut data: ResolutionCandidateMap<&'a str> = HashMap::default();
 
         let dic_id = if user { 1 } else { 0 };
 
diff --git a/sudachi/src/dic/character_category.rs b/sudachi/src/dic/character_category.rs
index 47d2bd15..cd2fafea 100644
--- a/sudachi/src/dic/character_category.rs
+++ b/sudachi/src/dic/character_category.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -149,10 +149,7 @@ impl CharacterCategory {
             }
 
             let mut categories = CategoryType::empty();
-            for elem in cols[1..]
-                .iter()
-                .take_while(|elem| elem.chars().next().unwrap() != '#')
-            {
+            for elem in cols[1..].iter().take_while(|elem| !elem.starts_with('#')) {
                 categories.insert(match elem.parse() {
                     Ok(t) => t,
                     Err(_) => {
@@ -292,7 +289,7 @@ impl Iterator for CharCategoryIter<'_> {
             (left..char::MAX, *self.categories.categories.last().unwrap())
         } else if self.current == 0 {
             let right = char::from_u32(*self.categories.boundaries.first().unwrap()).unwrap();
-            let r = (0 as char)..right as char;
+            let r = (0 as char)..right;
             (r, self.categories.categories[0])
         } else {
             let left = char::from_u32(self.categories.boundaries[self.current - 1]).unwrap();
diff --git a/sudachi/src/dic/dictionary.rs b/sudachi/src/dic/dictionary.rs
index 0092c21f..f5a7c429 100644
--- a/sudachi/src/dic/dictionary.rs
+++ b/sudachi/src/dic/dictionary.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -87,7 +87,6 @@ impl JapaneseDictionary {
 
         let plugins = {
             let grammar = &mut basic_dict.grammar;
-            let cfg = &*cfg;
             Plugins::load(cfg, grammar)?
         };
 
@@ -126,7 +125,6 @@ impl JapaneseDictionary {
 
         let plugins = {
             let grammar = &mut basic_dict.grammar;
-            let cfg = &*cfg;
             Plugins::load(cfg, grammar)?
         };
 
@@ -155,12 +153,12 @@ impl JapaneseDictionary {
     }
 
     /// Returns grammar with the correct lifetime
-    pub fn grammar<'a>(&'a self) -> &Grammar<'a> {
+    pub fn grammar(&self) -> &Grammar<'_> {
         &self._grammar
     }
 
     /// Returns lexicon with the correct lifetime
-    pub fn lexicon<'a>(&'a self) -> &LexiconSet<'a> {
+    pub fn lexicon(&self) -> &LexiconSet<'_> {
         &self._lexicon
     }
 
diff --git a/sudachi/src/dic/grammar.rs b/sudachi/src/dic/grammar.rs
index 1642a3bf..70da5ce3 100644
--- a/sudachi/src/dic/grammar.rs
+++ b/sudachi/src/dic/grammar.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,13 +27,15 @@ use nom::{
 };
 use std::ops::Index;
 
+type PosList = Vec<Vec<String>>;
+
 /// Dictionary grammar
 ///
 /// Contains part_of_speech list and connection cost map.
 /// It also holds character category.
 pub struct Grammar<'a> {
     _bytes: &'a [u8],
-    pub pos_list: Vec<Vec<String>>,
+    pub pos_list: PosList,
     pub storage_size: usize,
 
     /// The mapping to overload cost table
@@ -163,7 +165,7 @@ impl<'a> Grammar<'a> {
     }
 }
 
-fn pos_list_parser(input: &[u8]) -> SudachiNomResult<&[u8], Vec<Vec<String>>> {
+fn pos_list_parser(input: &[u8]) -> SudachiNomResult<&[u8], PosList> {
     let (rest, pos_size) = le_u16(input)?;
     nom::multi::count(
         nom::multi::count(utf16_string_parser, POS_DEPTH),
@@ -171,10 +173,7 @@ fn pos_list_parser(input: &[u8]) -> SudachiNomResult<&[u8], Vec<Vec<String>>> {
     )(rest)
 }
 
-fn grammar_parser(
-    input: &[u8],
-    offset: usize,
-) -> SudachiNomResult<&[u8], (Vec<Vec<String>>, i16, i16)> {
+fn grammar_parser(input: &[u8], offset: usize) -> SudachiNomResult<&[u8], (PosList, i16, i16)> {
     nom::sequence::preceded(
         take(offset),
         nom::sequence::tuple((pos_list_parser, le_i16, le_i16)),
@@ -259,14 +258,11 @@ mod tests {
         storage
     }
     fn string_to_bytes(s: &str) -> Vec<u8> {
-        s.encode_utf16()
-            .map(|c| c.to_le_bytes())
-            .flatten()
-            .collect()
+        s.encode_utf16().flat_map(|c| c.to_le_bytes()).collect()
     }
-    fn build_partofspeech(storage: &mut Vec<u8>) -> () {
+    fn build_partofspeech(storage: &mut Vec<u8>) {
         // number of part of speech
-        storage.extend(&(3 as i16).to_le_bytes());
+        storage.extend(&3_i16.to_le_bytes());
 
         storage.extend(
             b"\x07B\x00O\x00S\x00/\x00E\x00O\x00S\x00\x01*\x00\x01*\x00\x01*\x00\x01*\x00\x01*\x00",
@@ -287,20 +283,20 @@ mod tests {
         storage.extend(b"\x06");
         storage.extend(string_to_bytes("終止形-一般"));
     }
-    fn build_connect_table(storage: &mut Vec<u8>) -> () {
-        storage.extend(&(3 as i16).to_le_bytes());
-        storage.extend(&(3 as i16).to_le_bytes());
+    fn build_connect_table(storage: &mut Vec<u8>) {
+        storage.extend(&3_i16.to_le_bytes());
+        storage.extend(&3_i16.to_le_bytes());
 
-        storage.extend(&(0 as i16).to_le_bytes());
-        storage.extend(&(-300 as i16).to_le_bytes());
-        storage.extend(&(300 as i16).to_le_bytes());
+        storage.extend(&0_i16.to_le_bytes());
+        storage.extend(&(-300_i16).to_le_bytes());
+        storage.extend(&300_i16.to_le_bytes());
 
-        storage.extend(&(300 as i16).to_le_bytes());
-        storage.extend(&(-500 as i16).to_le_bytes());
-        storage.extend(&(-100 as i16).to_le_bytes());
+        storage.extend(&300_i16.to_le_bytes());
+        storage.extend(&(-500_i16).to_le_bytes());
+        storage.extend(&(-100_i16).to_le_bytes());
 
-        storage.extend(&(-3000 as i16).to_le_bytes());
-        storage.extend(&(200 as i16).to_le_bytes());
-        storage.extend(&(2000 as i16).to_le_bytes());
+        storage.extend(&(-3000_i16).to_le_bytes());
+        storage.extend(&200_i16.to_le_bytes());
+        storage.extend(&2000_i16.to_le_bytes());
     }
 }
diff --git a/sudachi/src/dic/header.rs b/sudachi/src/dic/header.rs
index 3df2e0e5..306dd0e3 100644
--- a/sudachi/src/dic/header.rs
+++ b/sudachi/src/dic/header.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -119,6 +119,12 @@ pub struct Header {
     pub description: String,
 }
 
+impl Default for Header {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
 impl Header {
     const DESCRIPTION_SIZE: usize = 256;
     pub const STORAGE_SIZE: usize = 8 + 8 + Header::DESCRIPTION_SIZE;
@@ -164,21 +170,21 @@ impl Header {
 
     /// Returns if this header version has grammar
     pub fn has_grammar(&self) -> bool {
-        match self.version {
-            HeaderVersion::SystemDict(_) => true,
-            HeaderVersion::UserDict(UserDictVersion::Version2) => true,
-            HeaderVersion::UserDict(UserDictVersion::Version3) => true,
-            _ => false,
-        }
+        matches!(
+            self.version,
+            HeaderVersion::SystemDict(_)
+                | HeaderVersion::UserDict(UserDictVersion::Version2)
+                | HeaderVersion::UserDict(UserDictVersion::Version3)
+        )
     }
 
     /// Returns if this header version has synonym group ids
     pub fn has_synonym_group_ids(&self) -> bool {
-        match self.version {
-            HeaderVersion::SystemDict(SystemDictVersion::Version2) => true,
-            HeaderVersion::UserDict(UserDictVersion::Version3) => true,
-            _ => false,
-        }
+        matches!(
+            self.version,
+            HeaderVersion::SystemDict(SystemDictVersion::Version2)
+                | HeaderVersion::UserDict(UserDictVersion::Version3)
+        )
     }
 
     pub fn write_to<W: Write>(&self, w: &mut W) -> SudachiResult<usize> {
@@ -191,7 +197,7 @@ impl Header {
 
         w.write_all(&self.version.to_u64().to_le_bytes())?;
         w.write_all(&self.create_time.to_le_bytes())?;
-        w.write_all(&self.description.as_bytes())?;
+        w.write_all(self.description.as_bytes())?;
         for _ in 0..Header::DESCRIPTION_SIZE - self.description.len() {
             w.write_all(&[0])?;
         }
@@ -204,7 +210,7 @@ fn nul_terminated_str_from_slice(buf: &[u8]) -> String {
     let str_bytes: &[u8] = if let Some(nul_idx) = buf.iter().position(|b| *b == 0) {
         &buf[..nul_idx]
     } else {
-        &buf
+        buf
     };
     String::from_utf8_lossy(str_bytes).to_string()
 }
diff --git a/sudachi/src/dic/lexicon/mod.rs b/sudachi/src/dic/lexicon/mod.rs
index 20043e81..f6e9f599 100644
--- a/sudachi/src/dic/lexicon/mod.rs
+++ b/sudachi/src/dic/lexicon/mod.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -110,7 +110,7 @@ impl<'a> Lexicon<'a> {
 
     #[inline]
     fn word_id(&self, raw_id: u32) -> WordId {
-        return WordId::new(self.lex_id, raw_id);
+        WordId::new(self.lex_id, raw_id)
     }
 
     /// Returns an iterator of word_id and end of words that matches given input
diff --git a/sudachi/src/dic/lexicon/trie.rs b/sudachi/src/dic/lexicon/trie.rs
index d6e76c31..2dfc0d3e 100644
--- a/sudachi/src/dic/lexicon/trie.rs
+++ b/sudachi/src/dic/lexicon/trie.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -100,7 +100,7 @@ impl<'a> Trie<'a> {
     }
 
     pub fn total_size(&self) -> usize {
-        4 * self.array.len() as usize
+        4 * self.array.len()
     }
 
     #[inline]
diff --git a/sudachi/src/dic/lexicon/word_id_table.rs b/sudachi/src/dic/lexicon/word_id_table.rs
index 48683497..bd79cfc5 100644
--- a/sudachi/src/dic/lexicon/word_id_table.rs
+++ b/sudachi/src/dic/lexicon/word_id_table.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -39,10 +39,10 @@ impl<'a> WordIdTable<'a> {
     #[inline]
     pub fn entries(&self, index: usize) -> WordIdIter {
         debug_assert!(index < self.bytes.len());
-        let ptr = unsafe { self.bytes.as_ptr().offset((index + self.offset) as isize) };
+        let ptr = unsafe { self.bytes.as_ptr().add(index + self.offset) };
         let cnt = unsafe { ptr.read() } as usize;
         let data_ptr = unsafe { ptr.offset(1) } as *const u32;
-        debug_assert!(index + cnt * std::mem::size_of::<u32>() + 1 <= self.bytes.len());
+        debug_assert!(index + cnt * std::mem::size_of::<u32>() < self.bytes.len());
         WordIdIter {
             data: unsafe { NonNull::new_unchecked(data_ptr as _) },
             remaining: cnt,
diff --git a/sudachi/src/dic/lexicon_set.rs b/sudachi/src/dic/lexicon_set.rs
index 32ffde1d..3a00ab68 100644
--- a/sudachi/src/dic/lexicon_set.rs
+++ b/sudachi/src/dic/lexicon_set.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -116,8 +116,8 @@ impl LexiconSet<'_> {
             let pos_id = word_info.pos_id as usize;
             if dict_id > 0 && pos_id >= self.num_system_pos {
                 // user defined part-of-speech
-                word_info.pos_id = (pos_id as usize - self.num_system_pos
-                    + self.pos_offsets[dict_id as usize]) as u16;
+                word_info.pos_id =
+                    (pos_id - self.num_system_pos + self.pos_offsets[dict_id as usize]) as u16;
             }
         }
 
diff --git a/sudachi/src/dic/mod.rs b/sudachi/src/dic/mod.rs
index 8de28bc4..95374299 100644
--- a/sudachi/src/dic/mod.rs
+++ b/sudachi/src/dic/mod.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -77,10 +77,7 @@ impl<'a> LoadedDictionary<'a> {
         character_category_file: &Path,
     ) -> SudachiResult<LoadedDictionary<'a>> {
         let character_category = CharacterCategory::from_file(character_category_file)?;
-        Ok(Self::from_system_dictionary_and_chardef(
-            dictionary_bytes,
-            character_category,
-        )?)
+        Self::from_system_dictionary_and_chardef(dictionary_bytes, character_category)
     }
 
     /// Creates a system dictionary from bytes, and load embedded default character category
@@ -88,10 +85,7 @@ impl<'a> LoadedDictionary<'a> {
         dictionary_bytes: &'a [u8],
     ) -> SudachiResult<LoadedDictionary<'a>> {
         let character_category = CharacterCategory::from_bytes(DEFAULT_CHAR_DEF_BYTES)?;
-        Ok(Self::from_system_dictionary_and_chardef(
-            dictionary_bytes,
-            character_category,
-        )?)
+        Self::from_system_dictionary_and_chardef(dictionary_bytes, character_category)
     }
 
     #[cfg(test)]
@@ -103,7 +97,9 @@ impl<'a> LoadedDictionary<'a> {
         let lexicon = other.lexicon;
         let grammar = other.grammar;
         self.lexicon_set.append(lexicon, npos)?;
-        grammar.map(|g| self.grammar.merge(g));
+        if let Some(g) = grammar {
+            self.grammar.merge(g)
+        }
         Ok(self)
     }
 }
@@ -140,6 +136,7 @@ pub struct DictionaryLoader<'a> {
 impl<'a> DictionaryLoader<'a> {
     /// Creates a binary dictionary from bytes
     ///
+    /// # Safety
     /// This function is marked unsafe because it does not perform header validation
     pub unsafe fn read_any_dictionary(dictionary_bytes: &[u8]) -> SudachiResult<DictionaryLoader> {
         let header = Header::parse(&dictionary_bytes[..Header::STORAGE_SIZE])?;
diff --git a/sudachi/src/dic/read/mod.rs b/sudachi/src/dic/read/mod.rs
index d218e8d3..494855e2 100644
--- a/sudachi/src/dic/read/mod.rs
+++ b/sudachi/src/dic/read/mod.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@ pub fn u32_array_parser(input: &[u8]) -> SudachiNomResult<&[u8], Vec<u32>> {
 
 pub fn u32_wid_array_parser(input: &[u8]) -> SudachiNomResult<&[u8], Vec<WordId>> {
     let (rest, length) = le_u8(input)?;
-    nom::multi::count(le_u32.map(|id| WordId::from_raw(id)), length as usize)(rest)
+    nom::multi::count(le_u32.map(WordId::from_raw), length as usize)(rest)
 }
 
 pub fn skip_wid_array(input: &[u8]) -> SudachiNomResult<&[u8], Vec<WordId>> {
diff --git a/sudachi/src/dic/storage.rs b/sudachi/src/dic/storage.rs
index b724ccbc..d11e4610 100644
--- a/sudachi/src/dic/storage.rs
+++ b/sudachi/src/dic/storage.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@ impl AsRef<[u8]> for Storage {
         match self {
             Storage::File(m) => m.as_bytes(),
             Storage::Borrowed(b) => b,
-            Storage::Owned(v) => &v,
+            Storage::Owned(v) => v,
         }
     }
 }
@@ -56,6 +56,8 @@ impl SudachiDicData {
         self.system.as_ref()
     }
 
+    /// # Safety
+    /// Call this function only after system dictionary data is ready.
     pub unsafe fn system_static_slice(&self) -> &'static [u8] {
         std::mem::transmute(self.system())
     }
diff --git a/sudachi/src/dic/word_id.rs b/sudachi/src/dic/word_id.rs
index 32240093..5de0e69a 100644
--- a/sudachi/src/dic/word_id.rs
+++ b/sudachi/src/dic/word_id.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -57,7 +57,7 @@ impl WordId {
         let dic_part = ((dic & 0xf) as u32) << 28;
         let word_part = word & WORD_MASK;
         let raw = dic_part | word_part;
-        return Self::from_raw(raw);
+        Self::from_raw(raw)
     }
 
     /// Creates the WordId with correctness checking
@@ -84,12 +84,12 @@ impl WordId {
 
     /// Extract Dictionary ID
     pub fn dic(&self) -> u8 {
-        return (self.raw >> 28) as u8;
+        (self.raw >> 28) as u8
     }
 
     /// Extract Word ID
     pub fn word(&self) -> u32 {
-        return self.raw & WORD_MASK;
+        self.raw & WORD_MASK
     }
 
     /// Check if the word comes from the system dictionary
@@ -99,10 +99,7 @@ impl WordId {
 
     /// Check if the word comes from the user dictionary
     pub fn is_user(&self) -> bool {
-        match self.dic() {
-            0 | 0xf => false,
-            _ => true,
-        }
+        !matches!(self.dic(), 0 | 0xf)
     }
 
     pub fn as_raw(&self) -> u32 {
diff --git a/sudachi/src/hash.rs b/sudachi/src/hash.rs
index 041f3460..96987b19 100644
--- a/sudachi/src/hash.rs
+++ b/sudachi/src/hash.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -91,7 +91,7 @@ impl Hasher for RoMuHash {
 
     #[inline(always)]
     fn write_u64(&mut self, i: u64) {
-        self.consume(i as u64);
+        self.consume(i);
     }
 
     #[inline(always)]
diff --git a/sudachi/src/input_text/buffer/mod.rs b/sudachi/src/input_text/buffer/mod.rs
index db2996af..9ac27d00 100644
--- a/sudachi/src/input_text/buffer/mod.rs
+++ b/sudachi/src/input_text/buffer/mod.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -34,19 +34,14 @@ const MAX_LENGTH: usize = u16::MAX as usize / 4 * 3;
 /// if the limit of the rewritten sentence is more than this number, then all bets are off
 const REALLY_MAX_LENGTH: usize = u16::MAX as usize;
 
-#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Eq, PartialEq, Debug, Clone, Default)]
 enum BufferState {
+    #[default]
     Clean,
     RW,
     RO,
 }
 
-impl Default for BufferState {
-    fn default() -> Self {
-        BufferState::Clean
-    }
-}
-
 /// InputBuffer - prepares the input data for the analysis
 ///
 /// By saying char we actually mean Unicode codepoint here.
@@ -112,7 +107,7 @@ impl InputBuffer {
     /// Creates input from the passed string. Should be used mostly for tests.
     ///
     /// Panics if the input string is too long.
-    pub fn from<'a, T: AsRef<str>>(data: T) -> InputBuffer {
+    pub fn from<T: AsRef<str>>(data: T) -> InputBuffer {
         let mut buf = Self::new();
         buf.reset().push_str(data.as_ref());
         buf.start_build().expect("");
diff --git a/sudachi/src/plugin/connect_cost/inhibit_connection.rs b/sudachi/src/plugin/connect_cost/inhibit_connection.rs
index c664280d..c25aba41 100644
--- a/sudachi/src/plugin/connect_cost/inhibit_connection.rs
+++ b/sudachi/src/plugin/connect_cost/inhibit_connection.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -83,8 +83,9 @@ mod tests {
         let right = 0;
         let bytes = build_mock_bytes();
         let mut grammar = build_mock_grammar(&bytes);
-        let mut plugin = InhibitConnectionPlugin::default();
-        plugin.inhibit_pairs = vec![(left, right)];
+        let plugin = InhibitConnectionPlugin {
+            inhibit_pairs: vec![(left, right)],
+        };
 
         plugin.edit(&mut grammar);
         assert_eq!(
@@ -96,10 +97,10 @@ mod tests {
     fn build_mock_bytes() -> Vec<u8> {
         let mut buf = Vec::new();
         // 0 - pos size, 1x1 connection with 0 element
-        buf.extend(&(0 as i16).to_le_bytes());
-        buf.extend(&(1 as i16).to_le_bytes());
-        buf.extend(&(1 as i16).to_le_bytes());
-        buf.extend(&(0 as i16).to_le_bytes());
+        buf.extend(&0_i16.to_le_bytes());
+        buf.extend(&1_i16.to_le_bytes());
+        buf.extend(&1_i16.to_le_bytes());
+        buf.extend(&0_i16.to_le_bytes());
         buf
     }
 
diff --git a/sudachi/src/plugin/connect_cost/mod.rs b/sudachi/src/plugin/connect_cost/mod.rs
index ad012354..eefbe335 100644
--- a/sudachi/src/plugin/connect_cost/mod.rs
+++ b/sudachi/src/plugin/connect_cost/mod.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@ impl PluginCategory for dyn EditConnectionCostPlugin {
 
     fn bundled_impl(name: &str) -> Option<Self::BoxType> {
         match name {
-            "InhibitConnectionPlugin" => Some(Box::new(InhibitConnectionPlugin::default())),
+            "InhibitConnectionPlugin" => Some(Box::<InhibitConnectionPlugin>::default()),
             _ => None,
         }
     }
diff --git a/sudachi/src/plugin/input_text/default_input_text/mod.rs b/sudachi/src/plugin/input_text/default_input_text/mod.rs
index 7bd53323..0f92ea56 100644
--- a/sudachi/src/plugin/input_text/default_input_text/mod.rs
+++ b/sudachi/src/plugin/input_text/default_input_text/mod.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -78,7 +78,7 @@ impl DefaultInputTextPlugin {
         for (i, line) in reader.lines().enumerate() {
             let line = line?;
             let line = line.trim();
-            if line.is_empty() || line.chars().next().unwrap() == '#' {
+            if line.is_empty() || line.starts_with('#') {
                 continue;
             }
             let cols: Vec<_> = line.split_whitespace().collect();
@@ -104,7 +104,7 @@ impl DefaultInputTextPlugin {
                 }
                 let first_char = cols[0].chars().next().unwrap();
                 let n_char = cols[0].chars().count();
-                if key_lengths.get(&first_char).map(|v| *v).unwrap_or(0) < n_char {
+                if key_lengths.get(&first_char).copied().unwrap_or(0) < n_char {
                     key_lengths.insert(first_char, n_char);
                 }
                 replace_char_map.insert(cols[0].to_string(), cols[1].to_string());
@@ -200,11 +200,8 @@ impl DefaultInputTextPlugin {
 
             // 2. handle normalization
             let need_lowercase = ch.is_uppercase();
-            let need_nkfc = !self.should_ignore(ch)
-                && match is_nfkc_quick(std::iter::once(ch)) {
-                    IsNormalized::Yes => false,
-                    _ => true,
-                };
+            let need_nkfc =
+                !self.should_ignore(ch) && is_nfkc_quick(std::iter::once(ch)) != IsNormalized::Yes;
 
             // iterator types are incompatible, so calls can't be moved outside branches
             match (need_lowercase, need_nkfc) {
@@ -238,14 +235,10 @@ impl DefaultInputTextPlugin {
         len: usize,
         ch: char,
     ) {
-        match data.next() {
-            Some(ch2) => {
-                if ch2 == ch {
-                    return;
-                }
+        if let Some(ch2) = data.next() {
+            if ch2 != ch {
                 replacer.replace_char_iter(start..start + len, ch2, data)
             }
-            None => return,
         }
     }
 }
@@ -266,7 +259,7 @@ impl InputTextPlugin for DefaultInputTextPlugin {
         );
 
         if rewrite_file_path.is_ok() {
-            let reader = BufReader::new(fs::File::open(&rewrite_file_path?)?);
+            let reader = BufReader::new(fs::File::open(rewrite_file_path?)?);
             self.read_rewrite_lists(reader)?;
         } else {
             let reader = BufReader::new(DEFAULT_REWRITE_DEF_BYTES);
@@ -286,10 +279,7 @@ impl InputTextPlugin for DefaultInputTextPlugin {
         edit: InputEditor<'a>,
     ) -> SudachiResult<InputEditor<'a>> {
         let chars = buffer.current_chars();
-        let need_nkfc = match is_nfkc_quick(chars.iter().cloned()) {
-            IsNormalized::Yes => false,
-            _ => true,
-        };
+        let need_nkfc = is_nfkc_quick(chars.iter().cloned()) != IsNormalized::Yes;
 
         let need_lowercase = chars.iter().any(|c| c.is_uppercase());
 
diff --git a/sudachi/src/plugin/input_text/mod.rs b/sudachi/src/plugin/input_text/mod.rs
index b0aee380..67f00d91 100644
--- a/sudachi/src/plugin/input_text/mod.rs
+++ b/sudachi/src/plugin/input_text/mod.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -70,9 +70,9 @@ impl PluginCategory for dyn InputTextPlugin {
 
     fn bundled_impl(name: &str) -> Option<Self::BoxType> {
         match name {
-            "IgnoreYomiganaPlugin" => Some(Box::new(IgnoreYomiganaPlugin::default())),
-            "DefaultInputTextPlugin" => Some(Box::new(DefaultInputTextPlugin::default())),
-            "ProlongedSoundMarkPlugin" => Some(Box::new(ProlongedSoundMarkPlugin::default())),
+            "IgnoreYomiganaPlugin" => Some(Box::<IgnoreYomiganaPlugin>::default()),
+            "DefaultInputTextPlugin" => Some(Box::<DefaultInputTextPlugin>::default()),
+            "ProlongedSoundMarkPlugin" => Some(Box::<ProlongedSoundMarkPlugin>::default()),
             _ => None,
         }
     }
diff --git a/sudachi/src/plugin/loader.rs b/sudachi/src/plugin/loader.rs
index e4d18d20..f874f95b 100644
--- a/sudachi/src/plugin/loader.rs
+++ b/sudachi/src/plugin/loader.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -75,7 +75,7 @@ fn system_specific_name(s: &str) -> Option<String> {
         let fname = p
             .file_name()
             .and_then(|np| np.to_str())
-            .map(|f| make_system_specific_name(f));
+            .map(make_system_specific_name);
         let parent = p.parent().and_then(|np| np.to_str());
         match (parent, fname) {
             (Some(p), Some(c)) => Some(format!("{}/{}", p, c)),
@@ -104,10 +104,10 @@ impl<'a, 'b, T: PluginCategory + ?Sized> PluginLoader<'a, 'b, T> {
     }
 
     pub fn freeze(self) -> PluginContainer<T> {
-        return PluginContainer {
+        PluginContainer {
             libraries: self.libraries,
             plugins: self.plugins,
-        };
+        }
     }
 
     fn load_plugin(&mut self, name: &str, plugin_cfg: &Value) -> SudachiResult<()> {
@@ -127,7 +127,7 @@ impl<'a, 'b, T: PluginCategory + ?Sized> PluginLoader<'a, 'b, T> {
                 self.load_plugin_from_dso(&candidates)?
             };
 
-        <T as PluginCategory>::do_setup(&mut plugin, plugin_cfg, &self.cfg, &mut self.grammar)
+        <T as PluginCategory>::do_setup(&mut plugin, plugin_cfg, self.cfg, self.grammar)
             .map_err(|e| e.with_context(format!("plugin {} setup", name)))?;
         self.plugins.push(plugin);
         Ok(())
@@ -231,9 +231,9 @@ pub trait PluginCategory {
 /// Helper function to load the plugins of a single category
 /// Should be called with turbofish syntax and trait object type:
 /// `let plugins = load_plugins_of::<dyn InputText>(...)`.
-pub fn load_plugins_of<'a, 'b, T: PluginCategory + ?Sized>(
+pub fn load_plugins_of<'a, T: PluginCategory + ?Sized>(
     cfg: &'a Config,
-    grammar: &'a mut Grammar<'b>,
+    grammar: &'a mut Grammar<'_>,
 ) -> SudachiResult<PluginContainer<T>> {
     let mut loader: PluginLoader<T> = PluginLoader::new(grammar, cfg);
     loader.load()?;
diff --git a/sudachi/src/plugin/oov/mecab_oov/mod.rs b/sudachi/src/plugin/oov/mecab_oov/mod.rs
index 8e2f3a8d..5c27524e 100644
--- a/sudachi/src/plugin/oov/mecab_oov/mod.rs
+++ b/sudachi/src/plugin/oov/mecab_oov/mod.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -47,7 +47,7 @@ const DEFAULT_UNK_DEF_BYTES: &[u8] = include_bytes!("../../../../../resources/un
 #[derive(Default)]
 pub struct MeCabOovPlugin {
     categories: HashMap<CategoryType, CategoryInfo, RoMu>,
-    oov_list: HashMap<CategoryType, Vec<OOV>, RoMu>,
+    oov_list: HashMap<CategoryType, Vec<Oov>, RoMu>,
 }
 
 /// Struct corresponds with raw config json file.
@@ -72,7 +72,7 @@ impl MeCabOovPlugin {
             let line = line?;
             let line = line.trim();
             if line.is_empty()
-                || line.chars().next().unwrap() == '#'
+                || line.starts_with('#')
                 || line.chars().take(2).collect::<Vec<_>>() == vec!['0', 'x']
             {
                 continue;
@@ -120,18 +120,21 @@ impl MeCabOovPlugin {
         categories: &HashMap<CategoryType, CategoryInfo, RoMu>,
         mut grammar: &mut Grammar,
         user_pos: UserPosMode,
-    ) -> SudachiResult<HashMap<CategoryType, Vec<OOV>, RoMu>> {
-        let mut oov_list: HashMap<CategoryType, Vec<OOV>, RoMu> = HashMap::with_hasher(RoMu::new());
+    ) -> SudachiResult<HashMap<CategoryType, Vec<Oov>, RoMu>> {
+        let mut oov_list: HashMap<CategoryType, Vec<Oov>, RoMu> = HashMap::with_hasher(RoMu::new());
         for (i, line) in reader.lines().enumerate() {
             let line = line?;
             let line = line.trim();
-            if line.is_empty() || line.chars().next().unwrap() == '#' {
+            if line.is_empty() || line.starts_with('#') {
                 continue;
             }
 
             let cols: Vec<_> = line.split(',').collect();
             if cols.len() < 10 {
-                return Err(SudachiError::InvalidDataFormat(i, format!("{}", line)));
+                return Err(SudachiError::InvalidDataFormat(
+                    i,
+                    format!("Invalid number of columns ({})", line),
+                ));
             }
             let category_type: CategoryType = cols[0].parse()?;
             if !categories.contains_key(&category_type) {
@@ -141,7 +144,7 @@ impl MeCabOovPlugin {
                 ));
             }
 
-            let oov = OOV {
+            let oov = Oov {
                 left_id: cols[1].parse()?,
                 right_id: cols[2].parse()?,
                 cost: cols[3].parse()?,
@@ -184,7 +187,7 @@ impl MeCabOovPlugin {
     }
 
     /// Creates a new oov node
-    fn get_oov_node(&self, oov: &OOV, start: usize, end: usize) -> Node {
+    fn get_oov_node(&self, oov: &Oov, start: usize, end: usize) -> Node {
         Node::new(
             start as u16,
             end as u16,
@@ -262,7 +265,7 @@ impl OovProviderPlugin for MeCabOovPlugin {
         );
 
         let categories = if char_def_path.is_ok() {
-            let reader = BufReader::new(fs::File::open(&char_def_path?)?);
+            let reader = BufReader::new(fs::File::open(char_def_path?)?);
             MeCabOovPlugin::read_character_property(reader)?
         } else {
             let reader = BufReader::new(DEFAULT_CHAR_DEF_BYTES);
@@ -276,7 +279,7 @@ impl OovProviderPlugin for MeCabOovPlugin {
         );
 
         let oov_list = if unk_def_path.is_ok() {
-            let reader = BufReader::new(fs::File::open(&unk_def_path?)?);
+            let reader = BufReader::new(fs::File::open(unk_def_path?)?);
             MeCabOovPlugin::read_oov(reader, &categories, grammar, settings.userPOS)?
         } else {
             let reader = BufReader::new(DEFAULT_UNK_DEF_BYTES);
@@ -311,7 +314,7 @@ struct CategoryInfo {
 
 /// The OOV definition
 #[derive(Debug, Default, Clone)]
-struct OOV {
+struct Oov {
     left_id: i16,
     right_id: i16,
     cost: i16,
diff --git a/sudachi/src/plugin/oov/mod.rs b/sudachi/src/plugin/oov/mod.rs
index 847f7c23..986fe8a0 100644
--- a/sudachi/src/plugin/oov/mod.rs
+++ b/sudachi/src/plugin/oov/mod.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -61,9 +61,9 @@ impl PluginCategory for dyn OovProviderPlugin {
 
     fn bundled_impl(name: &str) -> Option<Self::BoxType> {
         match name {
-            "SimpleOovPlugin" => Some(Box::new(SimpleOovPlugin::default())),
-            "MeCabOovPlugin" => Some(Box::new(MeCabOovPlugin::default())),
-            "RegexOovProvider" => Some(Box::new(RegexOovProvider::default())),
+            "SimpleOovPlugin" => Some(Box::<SimpleOovPlugin>::default()),
+            "MeCabOovPlugin" => Some(Box::<MeCabOovPlugin>::default()),
+            "RegexOovProvider" => Some(Box::<RegexOovProvider>::default()),
             _ => None,
         }
     }
diff --git a/sudachi/src/plugin/oov/regex_oov/mod.rs b/sudachi/src/plugin/oov/regex_oov/mod.rs
index ec0b68f4..1fdd1359 100644
--- a/sudachi/src/plugin/oov/regex_oov/mod.rs
+++ b/sudachi/src/plugin/oov/regex_oov/mod.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2022 Works Applications Co., Ltd.
+ *  Copyright (c) 2022-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -44,19 +44,14 @@ pub(crate) struct RegexOovProvider {
     boundaries: BoundaryMode,
 }
 
-#[derive(Deserialize, Eq, PartialEq, Debug, Copy, Clone)]
+#[derive(Deserialize, Eq, PartialEq, Debug, Copy, Clone, Default)]
 #[serde(rename_all = "lowercase")]
 pub enum BoundaryMode {
+    #[default]
     Strict,
     Relaxed,
 }
 
-impl Default for BoundaryMode {
-    fn default() -> Self {
-        BoundaryMode::Strict
-    }
-}
-
 fn default_max_length() -> usize {
     32
 }
@@ -89,7 +84,7 @@ impl OovProviderPlugin for RegexOovProvider {
     ) -> SudachiResult<()> {
         let mut parsed: RegexProviderConfig = serde_json::from_value(settings.clone())?;
 
-        if !parsed.regex.starts_with("^") {
+        if !parsed.regex.starts_with('^') {
             parsed.regex.insert(0, '^');
         }
 
diff --git a/sudachi/src/plugin/path_rewrite/join_numeric/mod.rs b/sudachi/src/plugin/path_rewrite/join_numeric/mod.rs
index 730b492b..d014781e 100644
--- a/sudachi/src/plugin/path_rewrite/join_numeric/mod.rs
+++ b/sudachi/src/plugin/path_rewrite/join_numeric/mod.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -102,10 +102,10 @@ impl JoinNumericPlugin {
                 for c in s.chars() {
                     if !parser.append(&c) {
                         if begin_idx >= 0 {
-                            if parser.error_state == numeric_parser::Error::COMMA {
+                            if parser.error_state == numeric_parser::Error::Comma {
                                 comma_as_digit = false;
                                 i = begin_idx - 1;
-                            } else if parser.error_state == numeric_parser::Error::POINT {
+                            } else if parser.error_state == numeric_parser::Error::Point {
                                 period_as_digit = false;
                                 i = begin_idx - 1;
                             }
@@ -132,8 +132,8 @@ impl JoinNumericPlugin {
                     i = begin_idx + 1;
                 } else {
                     let ss = path[i as usize - 1].word_info().normalized_form();
-                    if (parser.error_state == numeric_parser::Error::COMMA && ss == ",")
-                        || (parser.error_state == numeric_parser::Error::POINT && ss == ".")
+                    if (parser.error_state == numeric_parser::Error::Comma && ss == ",")
+                        || (parser.error_state == numeric_parser::Error::Point && ss == ".")
                     {
                         path =
                             self.concat(path, begin_idx as usize, i as usize - 1, &mut parser)?;
@@ -157,8 +157,8 @@ impl JoinNumericPlugin {
                 path = self.concat(path, begin_idx as usize, len, &mut parser)?;
             } else {
                 let ss = path[len - 1].word_info().normalized_form();
-                if (parser.error_state == numeric_parser::Error::COMMA && ss == ",")
-                    || (parser.error_state == numeric_parser::Error::POINT && ss == ".")
+                if (parser.error_state == numeric_parser::Error::Comma && ss == ",")
+                    || (parser.error_state == numeric_parser::Error::Point && ss == ".")
                 {
                     path = self.concat(path, begin_idx as usize, len - 1, &mut parser)?;
                 }
diff --git a/sudachi/src/plugin/path_rewrite/join_numeric/numeric_parser/mod.rs b/sudachi/src/plugin/path_rewrite/join_numeric/numeric_parser/mod.rs
index 30e61e1f..27c3c481 100644
--- a/sudachi/src/plugin/path_rewrite/join_numeric/numeric_parser/mod.rs
+++ b/sudachi/src/plugin/path_rewrite/join_numeric/numeric_parser/mod.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,10 +24,10 @@ use string_number::StringNumber;
 /// State of the parser
 #[derive(Debug, Eq, PartialEq)]
 pub enum Error {
-    NONE,
-    POINT,
-    COMMA,
-    // OTHER,
+    None,
+    Point,
+    Comma,
+    // Other,
 }
 
 /// Parses number written by arabic or kanji
@@ -83,7 +83,7 @@ impl NumericParser {
             is_first_digit: true,
             has_comma: false,
             has_hanging_point: false,
-            error_state: Error::NONE,
+            error_state: Error::None,
             total: StringNumber::new(),
             subtotal: StringNumber::new(),
             tmp: StringNumber::new(),
@@ -95,7 +95,7 @@ impl NumericParser {
         self.is_first_digit = true;
         self.has_comma = false;
         self.has_hanging_point = false;
-        self.error_state = Error::NONE;
+        self.error_state = Error::None;
         self.total.clear();
         self.subtotal.clear();
         self.tmp.clear();
@@ -105,15 +105,15 @@ impl NumericParser {
         if *c == '.' {
             self.has_hanging_point = true;
             if self.is_first_digit {
-                self.error_state = Error::POINT;
+                self.error_state = Error::Point;
                 return false;
             }
             if self.has_comma && !self.check_comma() {
-                self.error_state = Error::COMMA;
+                self.error_state = Error::Comma;
                 return false;
             }
             if !self.tmp.set_point() {
-                self.error_state = Error::POINT;
+                self.error_state = Error::Point;
                 return false;
             }
             self.has_comma = false;
@@ -121,7 +121,7 @@ impl NumericParser {
         }
         if *c == ',' {
             if !self.check_comma() {
-                self.error_state = Error::COMMA;
+                self.error_state = Error::Comma;
                 return false;
             }
             self.has_comma = true;
@@ -168,11 +168,11 @@ impl NumericParser {
     pub fn done(&mut self) -> bool {
         let ret = self.subtotal.add(&mut self.tmp) && self.total.add(&mut self.subtotal);
         if self.has_hanging_point {
-            self.error_state = Error::POINT;
+            self.error_state = Error::Point;
             return false;
         }
         if self.has_comma && self.digit_length != 3 {
-            self.error_state = Error::COMMA;
+            self.error_state = Error::Comma;
             return false;
         }
         ret
@@ -193,7 +193,7 @@ impl NumericParser {
     }
 
     fn is_small_unit(n: i32) -> bool {
-        -3 <= n && n < 0
+        (-3..0).contains(&n)
     }
     fn is_large_unit(n: i32) -> bool {
         n < -3
@@ -327,23 +327,23 @@ mod tests {
 
         parser.clear();
         assert!(!parse(&mut parser, "200,00,000"));
-        assert_eq!(Error::COMMA, parser.error_state);
+        assert_eq!(Error::Comma, parser.error_state);
 
         parser.clear();
         assert!(!parse(&mut parser, "2,4"));
-        assert_eq!(Error::COMMA, parser.error_state);
+        assert_eq!(Error::Comma, parser.error_state);
 
         parser.clear();
         assert!(!parse(&mut parser, "000,000"));
-        assert_eq!(Error::COMMA, parser.error_state);
+        assert_eq!(Error::Comma, parser.error_state);
 
         parser.clear();
         assert!(!parse(&mut parser, ",000"));
-        assert_eq!(Error::COMMA, parser.error_state);
+        assert_eq!(Error::Comma, parser.error_state);
 
         parser.clear();
         assert!(!parse(&mut parser, "256,55.1"));
-        assert_eq!(Error::COMMA, parser.error_state);
+        assert_eq!(Error::Comma, parser.error_state);
     }
 
     #[test]
@@ -360,10 +360,10 @@ mod tests {
 
         parser.clear();
         assert!(!parse(&mut parser, "6."));
-        assert_eq!(Error::POINT, parser.error_state);
+        assert_eq!(Error::Point, parser.error_state);
 
         parser.clear();
         assert!(!parse(&mut parser, "1.2.3"));
-        assert_eq!(Error::POINT, parser.error_state);
+        assert_eq!(Error::Point, parser.error_state);
     }
 }
diff --git a/sudachi/src/plugin/path_rewrite/join_numeric/numeric_parser/string_number.rs b/sudachi/src/plugin/path_rewrite/join_numeric/numeric_parser/string_number.rs
index 5c8d0ea4..eaf86e60 100644
--- a/sudachi/src/plugin/path_rewrite/join_numeric/numeric_parser/string_number.rs
+++ b/sudachi/src/plugin/path_rewrite/join_numeric/numeric_parser/string_number.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -97,7 +97,7 @@ impl StringNumber {
     }
 
     pub fn is_zero(&self) -> bool {
-        self.significand.len() == 0
+        self.significand.is_empty()
     }
 
     pub fn to_string(&mut self) -> String {
@@ -121,7 +121,7 @@ impl StringNumber {
                 .count();
             self.significand
                 .truncate(self.significand.len() - n_last_zero);
-            if self.significand.chars().last().unwrap() == '.' {
+            if self.significand.ends_with('.') {
                 self.significand.truncate(self.significand.len() - 1);
             }
         }
diff --git a/sudachi/src/plugin/path_rewrite/mod.rs b/sudachi/src/plugin/path_rewrite/mod.rs
index 64c86d4e..df0d2919 100644
--- a/sudachi/src/plugin/path_rewrite/mod.rs
+++ b/sudachi/src/plugin/path_rewrite/mod.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -53,8 +53,8 @@ impl PluginCategory for dyn PathRewritePlugin {
 
     fn bundled_impl(name: &str) -> Option<Self::BoxType> {
         match name {
-            "JoinNumericPlugin" => Some(Box::new(JoinNumericPlugin::default())),
-            "JoinKatakanaOovPlugin" => Some(Box::new(JoinKatakanaOovPlugin::default())),
+            "JoinNumericPlugin" => Some(Box::<JoinNumericPlugin>::default()),
+            "JoinKatakanaOovPlugin" => Some(Box::<JoinKatakanaOovPlugin>::default()),
             _ => None,
         }
     }
diff --git a/sudachi/src/sentence_detector.rs b/sudachi/src/sentence_detector.rs
index 805b1888..29285c76 100644
--- a/sudachi/src/sentence_detector.rs
+++ b/sudachi/src/sentence_detector.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -76,6 +76,12 @@ pub struct SentenceDetector {
     limit: usize,
 }
 
+impl Default for SentenceDetector {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
 impl SentenceDetector {
     pub fn new() -> Self {
         SentenceDetector {
@@ -175,13 +181,13 @@ fn parenthesis_level(s: &str) -> SudachiResult<usize> {
         ))
         .unwrap();
     }
-    let mut level = 0;
+    let mut level: usize = 0;
     for caps in PARENTHESIS.captures_iter(s) {
-        if let Some(_) = caps?.get(1) {
+        if caps?.get(1).is_some() {
             // open
             level += 1;
-        } else if level > 0 {
-            level -= 1;
+        } else {
+            level = level.saturating_sub(1);
         }
     }
     Ok(level)
@@ -225,7 +231,7 @@ fn is_continuous_phrase(s: &str, eos: usize) -> SudachiResult<bool> {
     }
 
     // we can safely unwrap since eos < s.len()
-    let c = s[eos..].chars().nth(0).unwrap();
+    let c = s[eos..].chars().next().unwrap();
     Ok((c == 'と' || c == 'や' || c == 'の') && EOS_ITEMIZE_HEADER.is_match(&s[..eos])?)
 }
 
diff --git a/sudachi/src/sentence_splitter.rs b/sudachi/src/sentence_splitter.rs
index 5fa77b7c..cd465951 100644
--- a/sudachi/src/sentence_splitter.rs
+++ b/sudachi/src/sentence_splitter.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -56,6 +56,12 @@ pub struct SentenceSplitter<'a> {
     checker: Option<NonBreakChecker<'a>>,
 }
 
+impl Default for SentenceSplitter<'_> {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
 impl SentenceSplitter<'_> {
     pub fn new() -> Self {
         SentenceSplitter {
diff --git a/sudachi/src/util/check_params.rs b/sudachi/src/util/check_params.rs
index aeab14e5..80ca1888 100644
--- a/sudachi/src/util/check_params.rs
+++ b/sudachi/src/util/check_params.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@ impl<'a> CheckParams for Grammar<'a> {
                 format!("max grammar leftId is {}", self.conn_matrix().num_left()),
             ));
         }
-        return Ok(x as u16);
+        Ok(x as u16)
     }
 
     fn check_right_id<T: Into<i64>>(&self, raw: T) -> SudachiResult<u16> {
@@ -57,7 +57,7 @@ impl<'a> CheckParams for Grammar<'a> {
                 format!("max grammar rightId is {}", self.conn_matrix().num_right()),
             ));
         }
-        return Ok(x as u16);
+        Ok(x as u16)
     }
 
     fn check_cost<T: Into<i64>>(&self, raw: T) -> SudachiResult<i16> {
@@ -82,6 +82,6 @@ impl<'a> CheckParams for Grammar<'a> {
                 ),
             ));
         }
-        return Ok(x as i16);
+        Ok(x as i16)
     }
 }
diff --git a/sudachi/src/util/cow_array.rs b/sudachi/src/util/cow_array.rs
index 73d8556a..10cc079f 100644
--- a/sudachi/src/util/cow_array.rs
+++ b/sudachi/src/util/cow_array.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@ pub trait ReadLE {
 
 impl ReadLE for i16 {
     fn from_le_bytes(bytes: &[u8]) -> Result<Self, TryFromSliceError> {
-        bytes.try_into().map(|b| Self::from_le_bytes(b))
+        bytes.try_into().map(Self::from_le_bytes)
     }
 }
 
@@ -35,7 +35,7 @@ impl ReadLE for u32 {
     where
         Self: Sized,
     {
-        bytes.try_into().map(|b| Self::from_le_bytes(b))
+        bytes.try_into().map(Self::from_le_bytes)
     }
 }
 
@@ -111,7 +111,9 @@ impl<'a, T: ReadLE + Clone> CowArray<'a, T> {
             let slice: &[T] = self.storage.as_ref().unwrap().as_slice();
             self.slice = unsafe { std::mem::transmute(slice) };
         }
-        self.storage.as_mut().map(|s| s[offset] = value);
+        if let Some(s) = self.storage.as_mut() {
+            s[offset] = value;
+        }
     }
 }
 
diff --git a/sudachi/src/util/user_pos.rs b/sudachi/src/util/user_pos.rs
index 7290e07a..06a2f332 100644
--- a/sudachi/src/util/user_pos.rs
+++ b/sudachi/src/util/user_pos.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -20,17 +20,12 @@ use itertools::Itertools;
 use serde::Deserialize;
 use std::fmt::Display;
 
-#[derive(Eq, PartialEq, Deserialize, Clone, Copy, Debug)]
+#[derive(Eq, PartialEq, Deserialize, Clone, Copy, Debug, Default)]
 #[serde(rename_all = "lowercase")]
 pub enum UserPosMode {
-    Allow,
+    #[default]
     Forbid,
-}
-
-impl Default for UserPosMode {
-    fn default() -> Self {
-        UserPosMode::Forbid
-    }
+    Allow,
 }
 
 pub trait UserPosSupport {


Test patch:
diff --git a/sudachi/src/dic/build/lexicon/test.rs b/sudachi/src/dic/build/lexicon/test.rs
index 3a62c084..6039fc00 100644
--- a/sudachi/src/dic/build/lexicon/test.rs
+++ b/sudachi/src/dic/build/lexicon/test.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -105,7 +105,7 @@ fn parse_kyoto_ignored() {
     let entries = rdr.entries();
     assert_eq!(entries.len(), 1);
     let kyoto = &entries[0];
-    assert_eq!(false, kyoto.should_index());
+    assert!(!kyoto.should_index());
 }
 
 #[test]
@@ -155,9 +155,9 @@ fn parse_pos_exhausted() {
     let mut rdr = LexiconReader::new();
     let mut data = String::new();
     for i in 0..=MAX_POS_IDS + 1 {
-        write!(
+        writeln!(
             data,
-            "x,-1,-1,5293,京都,名詞,固有名詞,地名,一般,*,{},キョウト,京都,*,A,*,*,*,*\n",
+            "x,-1,-1,5293,京都,名詞,固有名詞,地名,一般,*,{},キョウト,京都,*,A,*,*,*,*",
             i
         )
         .unwrap()
diff --git a/sudachi/src/dic/build/test/mod.rs b/sudachi/src/dic/build/test/mod.rs
index 5710c42d..f2ee3e98 100644
--- a/sudachi/src/dic/build/test/mod.rs
+++ b/sudachi/src/dic/build/test/mod.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@ use crate::dic::DictionaryLoader;
 use crate::error::SudachiError;
 use std::io::sink;
 
-static MATRIX_10_10: &'static [u8] = include_bytes!("matrix_10x10.def");
+static MATRIX_10_10: &[u8] = include_bytes!("matrix_10x10.def");
 
 #[test]
 fn build_grammar() {
diff --git a/sudachi/src/dic/build/test/with_analysis.rs b/sudachi/src/dic/build/test/with_analysis.rs
index 18f94a4d..12898bd3 100644
--- a/sudachi/src/dic/build/test/with_analysis.rs
+++ b/sudachi/src/dic/build/test/with_analysis.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -78,14 +78,9 @@ impl ConfigTestSupport {
 
         let mut result = String::new();
 
-        loop {
-            match data.next() {
-                Some(x) => {
-                    write!(result, "{:?}{}", prev, sep).unwrap();
-                    prev = x;
-                }
-                None => break,
-            }
+        for x in data {
+            write!(result, "{:?}{}", prev, sep).unwrap();
+            prev = x;
         }
         write!(result, "{:?}", prev).unwrap();
         result
diff --git a/sudachi/src/plugin/oov/mecab_oov/test.rs b/sudachi/src/plugin/oov/mecab_oov/test.rs
index f46dc443..f5ca6550 100644
--- a/sudachi/src/plugin/oov/mecab_oov/test.rs
+++ b/sudachi/src/plugin/oov/mecab_oov/test.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -437,7 +437,7 @@ fn read_oov_with_too_few_columns() {
         &mut grammar,
         UserPosMode::Forbid,
     );
-    assert_matches!(result, Err(SudachiError::InvalidDataFormat(0, s)) if s == data);
+    assert_matches!(result, Err(SudachiError::InvalidDataFormat(0, s)) if s.contains(data));
 }
 
 #[test]
@@ -492,13 +492,13 @@ fn read_oov_with_category_not_in_character_property() {
 
 fn build_plugin() -> MeCabOovPlugin {
     let mut plugin = MeCabOovPlugin::default();
-    let oov1 = OOV {
+    let oov1 = Oov {
         right_id: -1,
         left_id: -1,
         cost: -1,
         pos_id: 1,
     };
-    let oov2 = OOV {
+    let oov2 = Oov {
         right_id: -1,
         left_id: -1,
         cost: -1,
diff --git a/sudachi/src/plugin/path_rewrite/join_katakana_oov/tests.rs b/sudachi/src/plugin/path_rewrite/join_katakana_oov/tests.rs
index afaac93b..2a26d632 100644
--- a/sudachi/src/plugin/path_rewrite/join_katakana_oov/tests.rs
+++ b/sudachi/src/plugin/path_rewrite/join_katakana_oov/tests.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -88,7 +88,10 @@ fn start_with_middle() {
 
 #[test]
 fn start_with_tail() {
-    let mut plugin = JoinKatakanaOovPlugin::default();
+    let plugin = JoinKatakanaOovPlugin {
+        min_length: 3,
+        ..Default::default()
+    };
     let text = build_text("アイウアイウアイ");
     let path = vec![
         build_node_aiu(0, 9, 5562),
@@ -96,7 +99,6 @@ fn start_with_tail() {
         build_node_ai(18, 24, 19594),
     ];
 
-    plugin.min_length = 3;
     let path = plugin
         .rewrite(&text, path, &Lattice::default())
         .expect("Failed to rewrite path");
@@ -105,8 +107,10 @@ fn start_with_tail() {
 
 #[test]
 fn with_noovbow() {
-    let mut plugin = JoinKatakanaOovPlugin::default();
-    plugin.min_length = 3;
+    let plugin = JoinKatakanaOovPlugin {
+        min_length: 3,
+        ..Default::default()
+    };
 
     let text = build_text("ァアイアイウ");
 
diff --git a/sudachi/src/util/testing.rs b/sudachi/src/util/testing.rs
index c1c4b860..520919ff 100644
--- a/sudachi/src/util/testing.rs
+++ b/sudachi/src/util/testing.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@ pub fn char_cats() -> CharacterCategory {
 pub fn build_mock_bytes() -> Vec<u8> {
     let mut buf = Vec::new();
     // encode pos for oov
-    buf.extend(&(1 as i16).to_le_bytes());
+    buf.extend(&1_i16.to_le_bytes());
     let pos = vec!["補助記号", "一般", "*", "*", "*", "*"];
     for s in pos {
         let utf16: Vec<_> = s.encode_utf16().collect();
@@ -42,8 +42,8 @@ pub fn build_mock_bytes() -> Vec<u8> {
         }
     }
     // set 10 for left and right id sizes
-    buf.extend(&(10 as i16).to_le_bytes());
-    buf.extend(&(10 as i16).to_le_bytes());
+    buf.extend(&10_i16.to_le_bytes());
+    buf.extend(&10_i16.to_le_bytes());
     for i in 0..10 {
         for j in 0..10 {
             let val = i * 100 + j;
diff --git a/sudachi/tests/common/mod.rs b/sudachi/tests/common/mod.rs
index 430dd462..969d618d 100644
--- a/sudachi/tests/common/mod.rs
+++ b/sudachi/tests/common/mod.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Works Applications Co., Ltd.
+ * Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -38,7 +38,7 @@ use sudachi::dic::subset::InfoSubset;
 
 pub fn dictionary_bytes_from_path<P: AsRef<Path>>(dictionary_path: P) -> SudachiResult<Vec<u8>> {
     let dictionary_path = dictionary_path.as_ref();
-    let dictionary_stat = fs::metadata(&dictionary_path)?;
+    let dictionary_stat = fs::metadata(dictionary_path)?;
     let mut dictionary_file = File::open(dictionary_path)?;
     let mut dictionary_bytes = Vec::with_capacity(dictionary_stat.len() as usize);
     dictionary_file.read_to_end(&mut dictionary_bytes)?;
@@ -59,9 +59,7 @@ lazy_static! {
             .resolved_system_dict()
             .expect("system dict failure");
 
-        let dictionary_bytes = dictionary_bytes_from_path(dictionary_path)
-            .expect("Failed to read dictionary from path");
-        dictionary_bytes
+        dictionary_bytes_from_path(dictionary_path).expect("Failed to read dictionary from path")
     };
     static ref USER_DICTIONARY_BYTES: Vec<Box<[u8]>> = {
         let mut bytes = Vec::with_capacity(TEST_CONFIG.user_dicts.len());
@@ -98,7 +96,7 @@ impl TestTokenizer {
     pub fn new() -> TestTokenizer {
         let dict = JapaneseDictionary::from_cfg(&TEST_CONFIG).expect("failed to make dictionary");
         let tok = StatelessTokenizer::new(Arc::new(dict));
-        return TestTokenizer { tok };
+        TestTokenizer { tok }
     }
 
     pub fn tokenize<'a>(
@@ -111,7 +109,7 @@ impl TestTokenizer {
     }
 
     pub fn dict(&self) -> &JapaneseDictionary {
-        &self.tok.as_dict()
+        self.tok.as_dict()
     }
 }
 
diff --git a/sudachi/tests/morpheme.rs b/sudachi/tests/morpheme.rs
index c13624d4..2fdb1937 100644
--- a/sudachi/tests/morpheme.rs
+++ b/sudachi/tests/morpheme.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -51,7 +51,7 @@ fn morpheme_attributes() {
     assert_eq!("京都", ms.get(0).normalized_form());
     assert_eq!("キョウト", ms.get(0).reading_form());
 
-    assert_eq!(false, ms.get(0).is_oov());
+    assert!(!ms.get(0).is_oov());
 
     assert_eq!(3, ms.get(0).word_id().word());
     assert_eq!(0, ms.get(0).dictionary_id());
diff --git a/sudachi/tests/regex_oov.rs b/sudachi/tests/regex_oov.rs
index 0d21f855..a1956153 100644
--- a/sudachi/tests/regex_oov.rs
+++ b/sudachi/tests/regex_oov.rs
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021 Works Applications Co., Ltd.
+ *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@ use std::ops::Deref;
 
 mod common;
 
-const REGEX_CONFIG: &'static [u8] = include_bytes!("resources/sudachi.regex.json");
+const REGEX_CONFIG: &[u8] = include_bytes!("resources/sudachi.regex.json");
 
 #[test]
 fn no_other_words() {