0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

科学と神々株式会社Advent Calendar 2024

Day 17

~Resultシステムの拡張とエラー設計~

Last updated at Posted at 2024-12-16

第17回:エラーハンドリング(前編)

~Resultシステムの拡張とエラー設計~

はじめに

効果的なエラー処理は、堅牢なシステムの基盤となります。今回はResultシステムの拡張とエラー値の設計について解説します。

Resultシステムの拡張

// 拡張Result型
pub struct ExtendedResult<T, E> {
    inner: Result<T, E>,
    context: ErrorContext,
    default_handler: Option<Box<dyn ErrorHandler<E>>>,
}

impl<T, E> ExtendedResult<T, E> {
    pub fn new(result: Result<T, E>) -> Self {
        Self {
            inner: result,
            context: ErrorContext::new(),
            default_handler: None,
        }
    }
    
    pub fn with_context(mut self, context: ErrorContext) -> Self {
        self.context = context;
        self
    }
    
    pub fn with_default_handler<H: ErrorHandler<E> + 'static>(
        mut self,
        handler: H,
    ) -> Self {
        self.default_handler = Some(Box::new(handler));
        self
    }
    
    pub fn unwrap_or_handle(self) -> Result<T, E> {
        match self.inner {
            Ok(value) => Ok(value),
            Err(error) => {
                if let Some(handler) = self.default_handler {
                    handler.handle(error, &self.context)
                } else {
                    Err(error)
                }
            }
        }
    }
}

// エラーコンテキスト
#[derive(Clone, Debug)]
pub struct ErrorContext {
    location: Location,
    timestamp: SystemTime,
    trace: Backtrace,
    metadata: HashMap<String, Value>,
}

impl ErrorContext {
    pub fn add_metadata<K, V>(&mut self, key: K, value: V)
    where
        K: Into<String>,
        V: Into<Value>,
    {
        self.metadata.insert(key.into(), value.into());
    }
}

デフォルトエラー値の設計

// デフォルトエラー値の定義
#[derive(Debug, Clone)]
pub enum SystemDefault<E> {
    Value(E),
    Handler(Box<dyn Fn() -> E + Send + Sync>),
}

// エラー型の拡張トレイト
pub trait ErrorExt: Sized {
    fn with_default(self, default: SystemDefault<Self>) -> DefaultError<Self>;
}

// デフォルトエラーラッパー
pub struct DefaultError<E> {
    inner: Option<E>,
    default: SystemDefault<E>,
}

impl<E> DefaultError<E> {
    pub fn unwrap(self) -> E {
        match self.inner {
            Some(error) => error,
            None => match &self.default {
                SystemDefault::Value(ref value) => value.clone(),
                SystemDefault::Handler(handler) => handler(),
            },
        }
    }
}

// エラー変換機構
pub trait ErrorConverter<From, To> {
    fn convert(&self, from: From) -> To;
}

pub struct DefaultErrorConverter<From, To> {
    converter: Box<dyn Fn(From) -> To + Send + Sync>,
}

impl<From, To> DefaultErrorConverter<From, To> {
    pub fn new<F>(converter: F) -> Self
    where
        F: Fn(From) -> To + Send + Sync + 'static,
    {
        Self {
            converter: Box::new(converter),
        }
    }
}

エラー変換機構

// エラー変換システム
pub struct ErrorConversionSystem {
    converters: HashMap<(TypeId, TypeId), Box<dyn Any + Send + Sync>>,
}

impl ErrorConversionSystem {
    pub fn register_converter<From, To>(
        &mut self,
        converter: DefaultErrorConverter<From, To>,
    )
    where
        From: 'static,
        To: 'static,
    {
        let key = (TypeId::of::<From>(), TypeId::of::<To>());
        self.converters.insert(key, Box::new(converter));
    }
    
    pub fn convert<From, To>(&self, from: From) -> Result<To, ConversionError>
    where
        From: 'static,
        To: 'static,
    {
        let key = (TypeId::of::<From>(), TypeId::of::<To>());
        
        if let Some(converter) = self.converters.get(&key) {
            if let Some(converter) = converter.downcast_ref::<DefaultErrorConverter<From, To>>() {
                Ok((converter.converter)(from))
            } else {
                Err(ConversionError::InvalidConverter)
            }
        } else {
            Err(ConversionError::NoConverter)
        }
    }
}

// マクロによる便利な構文
#[macro_export]
macro_rules! with_default {
    ($result:expr, $default:expr) => {
        ExtendedResult::new($result).with_default_handler(|e| Ok($default))
    };
}

実装例:カスタムエラー型の実装

// カスタムエラー型の実装例
#[derive(Debug, Clone)]
pub enum AppError {
    Database(DatabaseError),
    Validation(ValidationError),
    System(SystemError),
    #[default]
    Unknown,
}

impl AppError {
    pub fn as_database(&self) -> Option<&DatabaseError> {
        match self {
            Self::Database(err) => Some(err),
            _ => None,
        }
    }
    
    pub fn is_critical(&self) -> bool {
        matches!(self, Self::System(_))
    }
}

// エラーハンドリングシステム
pub struct ErrorHandlingSystem {
    conversion_system: ErrorConversionSystem,
    default_handlers: HashMap<TypeId, Box<dyn ErrorHandler<AppError>>>,
}

impl ErrorHandlingSystem {
    pub fn handle_error<E: Into<AppError>>(&self, error: E) -> Result<(), AppError> {
        let app_error = error.into();
        
        // クリティカルエラーの特別処理
        if app_error.is_critical() {
            return Err(app_error);
        }
        
        // デフォルトハンドラーの適用
        if let Some(handler) = self.get_handler(&app_error) {
            handler.handle(app_error, &ErrorContext::new())
        } else {
            Err(app_error)
        }
    }
}

// 使用例
async fn example_usage() -> Result<(), AppError> {
    let mut system = ErrorHandlingSystem::new();
    
    // データベース操作
    let result = database_operation()
        .map_err(AppError::Database)
        .with_default(SystemDefault::Value(AppError::Unknown));
        
    match result {
        Ok(value) => {
            // 正常処理
            Ok(())
        }
        Err(error) => {
            // エラー処理
            system.handle_error(error)
        }
    }
}

// デフォルト値を使用した例
fn process_with_default<T>(
    operation: impl FnOnce() -> Result<T, AppError>,
    default: T,
) -> T {
    with_default!(operation(), default).unwrap_or_handle().unwrap_or(default)
}

今回のまとめ

  1. 柔軟なResultシステムの拡張
  2. 効果的なデフォルトエラー値の設計
  3. 型安全なエラー変換機構
  4. 実用的なエラーハンドリングシステム

次回予告

第18回では、エラーハンドリングの後編として、エラーの伝播制御とリカバリーメカニズムについて解説します。

参考資料

  • Error Handling in Rust
  • Type-Safe Error Management
  • Error Recovery Patterns
  • Resilient System Design
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?