Rust 我如何避免类似方法中的代码重复,这些方法在许多点上差异很小?

Rust 我如何避免类似方法中的代码重复,这些方法在许多点上差异很小?,rust,duplicates,code-duplication,Rust,Duplicates,Code Duplication,我有一个非常数据驱动的程序,它包含不同类型的实体,它们的结构非常相似,只是在特定的地方有所不同 例如,每个实体都有一个可以更改的名称。下面演示这些方法的相似性: pub fn rename_blueprint( &mut self, ctx: &mut Context, db_handle: &Transaction, blueprint_id: Uuid, new_name: &str, ) -> Result<

我有一个非常数据驱动的程序,它包含不同类型的实体,它们的结构非常相似,只是在特定的地方有所不同

例如,每个实体都有一个可以更改的名称。下面演示这些方法的相似性:

pub fn rename_blueprint(
    &mut self,
    ctx: &mut Context,
    db_handle: &Transaction,
    blueprint_id: Uuid,
    new_name: &str,
) -> Result<(), DataError> {
    ctx.debug(format!(
        "Renaming blueprint {} to {}",
        blueprint_id, new_name
    ));
    self.assert_blueprint_exists(ctx, db_handle, blueprint_id)?;
    let mut stmt = db_handle
        .prepare("UPDATE `blueprints` SET `name` = ? WHERE `id` == ?")
        .on_err(|_| ctx.err("Unable to prepare update statement"))?;
    let changed_rows = stmt
        .execute(params![new_name.to_string(), blueprint_id])
        .on_err(|_| ctx.err("Unable to update name in database"))?;
    if changed_rows != 1 {
        ctx.err(format!("Invalid amount of rows changed: {}", changed_rows));
        return Err(DataError::InvalidChangeCount {
            changes: changed_rows,
            expected_changes: 1,
        });
    }
    ctx.blueprint_renamed(blueprint_id, new_name);
    Ok(())
}

pub fn rename_attribute(
    &mut self,
    ctx: &mut Context,
    db_handle: &Transaction,
    attribute_id: Uuid,
    new_name: &str,
) -> Result<(), DataError> {
    ctx.debug(format!(
        "Renaming attribute {} to {}",
        attribute_id, new_name
    ));
    self.assert_attribute_exists(ctx, db_handle, attribute_id)?;
    let mut stmt = db_handle
        .prepare("UPDATE `attributes` SET `name` = ? WHERE `id` == ?")
        .on_err(|_| ctx.err("Unable to prepare update statement"))?;
    let changed_rows = stmt
        .execute(params![new_name.to_string(), attribute_id])
        .on_err(|_| ctx.err("Unable to update name in database"))?;
    if changed_rows != 1 {
        ctx.err(format!("Invalid amount of rows changed: {}", changed_rows));
        return Err(DataError::InvalidChangeCount {
            changes: changed_rows,
            expected_changes: 1,
        });
    }
    ctx.attribute_renamed(attribute_id, new_name);
    Ok(())
}

我通常解决这类问题的方法是使用泛型。让调用者选择适当的类型

trait Entity {
    fn desc(&self) -> String;
}

impl Entity for Blueprint {
    // ...
}

pub fn rename<T>(/* ... */)
where
    T: Entity,
{
    // ...
}
trait实体{
fn desc(&self)->字符串;
}
蓝图的impl实体{
// ...
}
发布fn重命名(/*…*/)
哪里
T:实体,
{
// ...
}

Rust没有模板,但即使有了模板,它们如何解决这种特殊情况?你能举个例子吗?这里的问题不是选择类型,而是选择适当的字符串位和方法来与主方法一起使用。C++使用的术语很抱歉。我指的是铁锈中的仿制药。其中一种方法是编写一个泛型函数,该函数接受带有trait绑定的泛型类型参数T。让所有实体实现该特性绑定。虽然这不能解决代码重复问题,但只会更改代码的编写位置-我仍然需要为所有重命名函数编写一个实现,即使它们基本上非常复杂similar@Folling为什么它不能消除重复?通用/重复代码在函数
rename
中出现一次,不同的代码出现在每个trait实现中。复制仍然存在于何处?
rename
函数需要包含目前不同的
rename.*
函数中已经包含的几乎所有内容,因此仍然存在大量复制,它只存在于
impl
中,而不是当前的方法“代码几乎相同”-这里“几乎”的确切含义使一切都不同。也许可以展示一些例子,准确地解释这些方法是如何相同的,以及它们是如何不同的。@Shepmaster鉴于这是一个关于语义的问题,而不是关于错误或无效性的问题,我不认为MVCE会有什么帮助。对于代码中使用的类型、特征或字段,这个问题也是不可知的@如上所述,用任何其他类型的名称替换每个
blueprint
,是它们最大的区别。虽然我为同一个函数添加了一个不同类型的示例,但我同意这样一种观点,即它可以更清楚地看到我的确切问题-谢谢!相应地调整了我的代码。我更多的是寻找指导原则,而不是为我编写代码的人。一些可能的解决方案是:1。使用宏,然后使用类似于
entity\u rename\u impl!(args)
2。对于每个函数3中可能不同的特定对象,使用具有不同参数的帮助器方法。不要试图抽象整个方法,而是专注于为较小的对象编写帮助函数,这样这些方法可能会重复,但在其他地方抽象的代码非常少,我希望能够获得一些关于最佳实践的见解,而不是一个预先制作的解决方案。它们的主要区别是什么这是非常重要的。如果您使用这两段代码,您将看到有重要的语义差异。例如,您的代码块在SQL中有不同的条件,这些条件超出了名称的范围。
trait Entity {
    fn desc(&self) -> String;
}

impl Entity for Blueprint {
    // ...
}

pub fn rename<T>(/* ... */)
where
    T: Entity,
{
    // ...
}